1️⃣ 核心区别
| 特性 | struct + let(不可变数组) | class + var(可变数组) |
|---|---|---|
| 类型 | 值类型 | 引用类型 |
| 默认行为 | 赋值/传递会复制,修改生成新副本 | 赋值/传递是引用,共享同一对象 |
| 可变性 | 不可变,无法修改内容 | 可变,可随时修改 |
| 线程安全 | 默认线程安全(只读) | 默认不线程安全,需要加锁 |
| 性能 | 大型结构可能复制开销,但 Swift 做了优化(Copy-on-Write) | 修改无需复制,性能高,但要处理竞争条件 |
2️⃣ struct 不可变数组在并发环境
let numbers: [Int] = [1, 2, 3, 4]
// 并发访问
DispatchQueue.concurrentPerform(iterations: 4) { i in
print(numbers[i]) // 只读访问,没有副作用
}
特点:
-
数组是
let,不可修改 → 线程安全 -
Swift 的值类型在传递和赋值时会做 Copy-on-Write:
- 当没有修改时,不会复制,性能开销小
- 修改时才生成副本 → 并发读取仍然安全
✅ 结论:只读访问的 struct 不需要额外同步机制。
3️⃣ class 可变数组在并发环境
class NumberHolder {
var numbers: [Int] = [1, 2, 3, 4]
}
let holder = NumberHolder()
// 并发修改
DispatchQueue.concurrentPerform(iterations: 4) { i in
holder.numbers.append(i) // ❌ 竞态条件
}
问题:
-
数组是引用类型,多个线程共享同一对象
-
并发写操作会导致:
- 数据竞争(race condition)
- 崩溃或数据损坏
解决方法:
- 使用 DispatchQueue 串行访问:
let queue = DispatchQueue(label: "sync.queue")
queue.async {
holder.numbers.append(5)
}
- 使用 NSLock / DispatchSemaphore 保护访问:
let lock = NSLock()
DispatchQueue.global().async {
lock.lock()
holder.numbers.append(10)
lock.unlock()
}
4️⃣ 总结对比
| 特性 | struct + let(不可变数组) | class + var(可变数组) |
|---|---|---|
| 读操作 | 多线程安全 | 多线程安全 |
| 写操作 | 不可写,无需同步 | 多线程不安全,必须同步 |
| 性能 | Copy-on-Write,有时会复制 | 写操作直接修改,无复制,但加锁影响性能 |
| 适用场景 | 并发读取、函数式风格、数据不可变 | 需要共享可变状态,或高性能修改 |
💡 经验法则:
并发环境下,如果可以使用 struct + let → 优先选择不可变数组
避免共享可变状态带来的复杂性和 bug。