如何避免在swift中创建对象数组的副本

时间:2015-08-04 16:03:58

标签: arrays swift dictionary

我有一个包含一种对象的数组字典:数据库记录。 每个数组就像一个与字典中的键(表名)相关联的表。

我的问题是:如何更新表(删除记录,添加记录等)而不制作表的任何临时副本,就像我对NSArrays的NSDictionary一样?

目前,我这样做:

func addRecord(theRecord: DatabaseRecord, inTable tableName: String)
{
    if var table = self.database[tableName]
    {
        table.append(theRecord)
        self.database[tableName] = table
    }
    else
    {
        self.database[tableName] = [theRecord];
    }
}

问题是:表的临时副本是两次。这非常低效。

2 个答案:

答案 0 :(得分:3)

Swift数组使用值(复制)语义,但实际上它们被传递并指定为引用。只有在更改数组大小或重新排序元素时,才会真正复制数组。这被称为"懒惰副本"或"复制写入"。您不必害怕简单地分配这样的数组:

var temp = someBigArray 

复制。但是当你打电话时:

temp.append(someValue)

然后发生实际的复制。

在此行的代码中:

if var table = self.database[tableName]

数组复制。

在这一行:

table.append(theRecord)

被复制。

在这一行:

self.database[tableName] = table

复制,但字典中数组的引用计数减1,如果不存在对此数组的其他引用,则将其解除分配。 "表"然后将数组作为引用传递给" self.database [tableName]"。这里没有复制。

但Eric D的答案是正确的,通过直接在数组上调用append()(使用?运算符),创建的开销最小。这就是你应该这样做的方式:

self.database[tableName]?.append(theRecord)

但是也可以在append()中复制数组 - 如果必须扩展数组大小并且没有预先分配的空间,则会出现这种情况。这是因为数组总是必须有连续的内存。

因此,通过调用append(),可能会发生数组必须执行此操作:

  • 使用当前计数大小加上至少一个条目来分配新的连续内存块。 (通常它会分配更多,以防你将来追加更多)
  • 将整个内容复制到新的内存块(包括新条目)
  • 发布" old"内存(这实际上更复杂,因为其他数组可能引用旧内存,在这种情况下它没有被释放)

所以通常你不必关心"效率低下"数组的副本 - 编译器知道的更好。

答案 1 :(得分:2)

我认为这是一个可以使用?的可选绑定并直接附加值(这里使用三元运算符)的情况:

func addRecord(theRecord: DatabaseRecord, inTable tableName: String) {

    self.database[tableName] == nil ? self.database[tableName] = [theRecord] : self.database[tableName]?.append(theRecord)

}