我有一个包含一种对象的数组字典:数据库记录。 每个数组就像一个表,与字典中的一个键(表名)相关联。
我的问题是:如何更新表(删除记录、添加记录等)而不制作表的任何临时副本,就像我对 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];
}
}
问题是:表的临时副本创建了两次。这是非常低效的。
最佳答案
Swift 数组使用值(复制)语义,但实际上它们是作为引用传递和赋值的。只有当数组大小发生变化或元素重新排序时,数组才会真正被复制。这称为“延迟复制”或“写时复制”。您不必害怕像这样简单地分配一个数组:
var temp = someBigArray
它没有被复制。但是当你打电话时:
temp.append(someValue)
然后实际复制发生。
在您的代码中这一行:
if var table = self.database[tableName]
数组未被复制。
在这一行中:
table.append(theRecord)
它被复制了。
在这一行中:
self.database[tableName] = table
它未被复制,但字典中数组的引用计数减一,如果不存在对该数组的其他引用,则它被释放。 “表”数组然后作为引用传递给“self.database[tableName]”。这里没有复制。
但 Eric D 的回答是正确的,通过直接在数组上调用 append()(使用 ? 运算符),创建的开销最少。你应该这样做:
self.database[tableName]?.append(theRecord)
但是在 append() 中也可能发生复制数组的情况 - 如果必须扩展数组大小并且没有预分配空间,就会出现这种情况。这是因为数组必须始终具有连续的内存。
因此,通过调用 append(),数组可能必须这样做:
- 分配一个新的连续内存块,当前计数大小加上至少一个条目。 (通常它会分配更多,以防您将来追加更多)
- 将全部内容复制到新的内存块中(包括新的条目)
- 释放“旧”内存(这实际上更复杂,因为其他数组可能引用旧内存,在这种情况下它不会被释放)
所以通常你不必关心数组的“低效”副本——编译器知道得更多。
关于arrays - 如何避免快速创建对象数组的副本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31814095/