arrays - 如何避免快速创建对象数组的副本

标签 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];
    }
}

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

最佳答案

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/

相关文章:

iOS 框架和崩溃报告

UITableView 位于顶部

python - 使用自定义排序功能按键对字典列表进行排序

javascript - Angularjs 反转数组给出错误

c++ - 使用 gets(a) 而不是 cin.getline(a,20) 有什么好处?

javascript - VueJS 2 中的排序

php - 为什么我不能在 foreach 循环中将元素插入子数组?

ios - Swift DSL - 如何根据已知的常量数据创建平面字典?

c# - 查找值在字典中的范围

Python:将键添加到每个列表项,然后转换为字典