You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift如何避免枚举时集合被修改?底层原理解析

Swift与Objective-C枚举集合时修改行为差异的底层原理

一、代码行为对比

1. Swift正向/倒序遍历修改集合均正常

Swift中无论是正向还是倒序遍历可变数组时修改集合,代码都能正常执行:

正向遍历代码:

var arr = [1, 2, 3, 4, 5, 6]
for num in arr {
    arr.removeAll { val in
        return val == num
    }
}
print(arr) // 输出 []

倒序遍历代码:

var arr = [1, 2, 3, 4, 5, 6]
for num in arr.reversed(){
    arr.removeAll { val in
        return val == num
    }
}
print(arr) // 输出 []

2. Objective-C正向遍历修改集合崩溃,倒序遍历正常

Objective-C中正向遍历可变数组时修改集合会直接崩溃:

崩溃代码:

NSMutableArray * arr = [NSMutableArray arrayWithObjects: @11, @22, @33, @44, @55, @66, nil];
for (NSObject * obj in arr) {
    [arr removeObject: obj];
}
NSLog(@"%@", arr);

崩溃信息:

Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x> was mutated while being enumerated.'

但倒序遍历的变体代码可以正常运行:

NSMutableArray * arr = [NSMutableArray arrayWithObjects: @11, @22, @33, @44, @55, @66, nil];
for (NSObject * obj in arr.reverseObjectEnumerator) {
    [arr removeObject: obj];
}
NSLog(@"%@", arr); // 输出 []

二、核心问题

为什么Swift在枚举集合时修改原集合不会崩溃,而Objective-C正向遍历会崩溃?Swift的底层机制是什么?

三、底层原理分析

1. Objective-C正向遍历崩溃的原因

Objective-C的NSMutableArray是引用类型,for-in遍历依赖的NSEnumerator枚举器会直接绑定原数组的存储状态。数组内部维护了一个修改版本号,每次增删改操作都会更新这个版本号。枚举器在每次迭代时会校验版本号,如果发现版本号和枚举开始时不一致,就会抛出NSGenericException异常,阻止非法操作。

而倒序遍历使用reverseObjectEnumerator时,枚举器的遍历逻辑是从数组末尾向前取元素。每次删除末尾元素不会影响前面未遍历元素的索引位置,且枚举器的版本号校验不会触发,因此可以正常完成遍历和修改。

2. Swift避免崩溃的核心机制

Swift的Array是值类型,且遵循**写时复制(Copy-on-Write, COW)**机制,这是它避免枚举冲突的核心:

  • 当执行for num in arr时,遍历操作是对数组的只读访问,此时不会触发复制,遍历的是原数组的存储区域。
  • 当在循环中调用removeAll修改数组时,由于原存储区域正在被遍历(只读占用),Swift会自动复制一份新的存储区域,所有修改操作都在新区域上进行。
  • 遍历操作始终在原存储区域上执行,修改后的数组指向新存储区域,两者完全隔离,既不会影响遍历序列,也不会触发任何冲突或崩溃。

对于arr.reversed()的倒序遍历,reversed()返回的是ReversedCollection视图,它并不复制原数组,而是通过索引映射实现反向访问。但同样基于COW机制,当修改原数组时,会触发复制,遍历仍基于原存储的索引映射进行,因此也能正常执行。


内容的提问来源于stack exchange,提问作者DNG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 10:33:59