swift - 在迭代期间使用 forEach 从集合中删除元素

标签 swift

最近,我没有多想就写了这段代码:

myObject.myCollection.forEach { myObject.removeItem($0) }

其中 myObject.removeItem(_)myObject.myCollection 中删除项目。

现在看看代码,我很困惑为什么这会起作用 - 我不应该得到类似于 Collection was mutated while being enumerated 的异常吗? 相同的代码甚至在使用常规 for-in 循环时也能工作!

这是预期的行为还是我“幸运”它没有崩溃?

最佳答案

这确实是预期的行为 - 这是因为 Swift 中的Array(以及标准库中的许多其他集合)是一种具有写时复制语义的值类型。这意味着它的底层缓冲区(间接存储)将在发生变异时复制(并且,作为一种优化,仅当它没有被唯一引用时)。

当您要迭代序列(例如数组)时,可以使用forEach(_:)或标准for in 循环,从序列的 makeIterator() 创建一个迭代器方法及其 next()重复应用方法以顺序生成元素。

您可以将序列迭代视为如下所示:

let sequence = [1, 2, 3, 4]
var iterator = sequence.makeIterator()

// `next()` will return the next element, or `nil` if
//  it has reached the end sequence.
while let element = iterator.next() { 
    // do something with the element
}

对于数组IndexingIterator用作其迭代器 - 它将通过简单地存储该集合以及迭代的当前索引来迭代给定集合的元素。每次调用 next() 时,基集合都会使用索引作为下标,然后递增,直到达到 endIndex (您可以看到其 exact implementation here )。

因此,当您在循环中改变数组时,其底层缓冲区不是唯一引用的,因为迭代器也可以查看它。这会强制创建缓冲区的副本 - 然后 myCollection 使用该副本。

因此,现在有两个数组 - 一个正在迭代,另一个正在改变。只要 myCollection 的缓冲区保持唯一引用,循环中的任何进一步突变都不会触发另一个副本。

因此,这意味着在枚举集合时用值语义改变集合是完全安全的。枚举将迭代集合的整个长度 - 完全独立于您所做的任何突变,因为它们将在副本上完成。

关于swift - 在迭代期间使用 forEach 从集合中删除元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42055194/

相关文章:

swift - Alamofire 和 PromiseKit 返回 Promise<[T]>

swift - 我的 Sprite 不执行我告诉它的操作?

ios - 如何在 iOS 中显示带有天气信息的通知横幅?

swift - 关联值和原始值可以在 Swift 枚举中共存吗?

ios - 为分组 TableView 填充 cellForRowAtIndexPath

swift - 如何在 Xcode 之外运行 Xcode 应用程序? MacOS 独立应用程序的运行方式与 Xcode Simulator 中的应用程序不同

swift - UI 快速更改,CoreAnimation : warning, 删除了未提交 CATransaction 的线程

ios - 如何在应用程序强制退出和应用程序重新启动后恢复 NSURLSession 下载过程?

swift - 如何以编程方式更改约束常量?

swift - 如何获取 URL "title"标记值?