为什么需要"原子"操作?
Swift 没有现成的 atomic
关键字。当多个线程/任务同时读写同一属性或集合时,会出现:
- 读到中间状态(数组越界、字典重复 key)
- 丢失更新(值类型复制-修改-写回)
使用Swift 实现Atomic有多种方案,以下从简单到高级介绍iOS17及以下系统的可行方案;最后补充 iOS 18 原生 Synchronization
框架的替代思路。
方案 1:属性包装器 + GCD(入门级)
swift
@propertyWrapper
struct Atomic<Value> {
private let queue = DispatchQueue(label: "atomic.queue") // 串行队列
private var storage: Value
init(wrappedValue: Value) { storage = wrappedValue }
var wrappedValue: Value {
// 串行队列同步执行,类似锁机制,读写都需要排队
get { queue.sync { storage } }
set { queue.sync { storage = newValue } } // 注意:非 barrier
}
}
使用
swift
class Client {
@Atomic var counter = 0
}
踩坑:复合运算"非原子"
swift
let client = Client()
client.counter += 1 // 读 + 改 + 写 三步,线程不安全!
方案 2:暴露 modify
闭包(中级)
swift
@propertyWrapper
class Atomic<Value> { // 改为 class,避免值拷贝
private let queue = DispatchQueue(label: "atomic.queue") // 串行队列
private var storage: Value
var projectedValue: Atomic<Value> { self }
init(wrappedValue: Value) { storage = wrappedValue }
var wrappedValue: Value {
// 串行队列同步执行,类似锁机制,读写都需要排队
// 这里对应整体取值和整体赋值
get { queue.sync { storage } }
set { queue.sync { storage = newValue } }
}
// 原子"读-改-写"
// 这里对应复合运算
func modify(_ block: (inout Value) -> Void) {
queue.sync { block(&storage) } // 整个 block 在串行队列里执行
}
}
使用
swift
client.$counter.modify { $0 += 1 } // ✅ 线程安全
client.$counter.modify { $0.append(42) } // 同样适用于数组
方案 3:独立 Atomic<T>
类(高级,API 更友好)
swift
final class Atomic<Value> {
private var value: Value
private let queue = DispatchQueue(label: "atomic.queue", attributes: .concurrent) // 并发队列
init(_ value: Value) { self.value = value }
// 1. 只读快照
func read<T>(_ keyPath: KeyPath<Value, T>) -> T {
queue.sync { value[keyPath: keyPath] }
}
// 2. 读并转换
func read<T>(_ transform: (Value) throws -> T) rethrows -> T {
try queue.sync { try transform(value) }
}
// 3. 整体替换
func modify(_ newValue: Value) {
queue.async(flags: .barrier) { self.value = newValue }
}
// 4. 原子读-改-写
func modify(_ transform: (inout Value) -> Void) {
queue.sync(flags: .barrier) { transform(&value) }
}
}
使用
swift
let num = Atomic(0)
num.modify { $0 += 1 }
print(num.read(\.self)) // 1
let arr = Atomic([1, 2, 3])
arr.modify { $0.append(4) }
print(arr.read { $0.contains(4) }) // true
并发队列 + barrier 保证"写"互斥,"读"并行,性能优于串行队列。
方案 4:专用原子集合(线程安全数组)
swift
class AtomicArray<Element> {
private var storage: [Element] = []
private let queue = DispatchQueue(label: "atomic.array", attributes: .concurrent)
subscript(index: Int) -> Element {
get { queue.sync { storage[index] } }
set { queue.async(flags: .barrier) { self.storage[index] = newValue } }
}
func append(_ new: Element) {
queue.async(flags: .barrier) { self.storage.append(new) }
}
func removeAll() {
queue.async(flags: .barrier) { self.storage.removeAll() }
}
func forEach(_ body: (Element) throws -> Void) rethrows {
try queue.sync { try storage.forEach(body) }
}
func all() -> [Element] { queue.sync { storage } }
}
压测:10 线程并发 append
swift
Task.detached {
let arr = AtomicArray<Int>()
DispatchQueue.concurrentPerform(iterations: 10_000) { @Sendable in arr.append($0)}
print(arr.all().count) // 10_000 ✅
}✅
方案 5:原子字典(读多写少最优解)
swift
class AtomicDictionary<Key: Hashable, Value> {
private var dict: [Key: Value] = [:]
private let queue = DispatchQueue(label: "atomic.dict", attributes: .concurrent)
subscript(key: Key) -> Value? {
get { queue.sync { dict[key] } }
set { queue.async(flags: .barrier) { self.dict[key] = newValue } }
}
subscript(key: Key, default defaultValue: @autoclosure () -> Value) -> Value {
get {
queue.sync { dict[key] ?? defaultValue() }
}
set {
queue.async(flags: .barrier) { self.dict[key] = newValue }
}
}
}
方案 6 _read
/ _modify
原生读写访问器
⚠️ 下划线 API,非稳定,仅用于实验。
swift
@propertyWrapper
struct Atomic<Value> {
private var storage: Value
private let lock = NSLock()
var wrappedValue: Value {
get { lock.lock(); defer { lock.unlock() }; return storage }
_modify { // 原生"产出"引用,零拷贝
lock.lock()
defer { lock.unlock() }
yield &storage
}
}
}
优点:
- 无队列调度,纯锁+直接内存引用,性能更高。
缺点:
- 未来可能改名或移除;不建议上生产。
iOS 18+ 新选择:Apple Synchronization
框架
swift
import Synchronization
let counter = Atomic<Int>(0) // 系统级原子类型,无锁、CPU 指令级
counter.wrappingIncrement(by: 1)
- 真正无锁(使用
std::atomic
底层)。 - 支持泛型、Sendable、wait/notify 等高级语义。
- 最低部署版本 iOS 18;若需向下兼容,仍需本文方案。
选型速查表
场景 | 推荐方案 |
---|---|
只支持 iOS 18+ | 系统 Synchronization |
属性读写,iOS 17 及以下 | 方案 3 Atomic<Value> 类 |
专用集合(数组/字典) | 方案 4 / 5 原子集合类 |
超高性能、实验 | 方案 6 _read / _modify |
结论
- Swift 没有"官方原子"≠ 不能写出线程安全的代码。
- GCD + barrier + 值类型拷贝 足以覆盖 99 % 业务需求。
- 提前布局:当 iOS 18 普及后,迁移到
Synchronization
只需换一行 import。
记住口诀:
"读并行、写互斥,修改用 barrier,集合要包类。"