咨询Swift中替代objc_sync_enter/exit实现读多写少临界区的现代方案
背景
你之前通过objc_sync_enter和objc_sync_exit模拟Objective-C的@synchronized,但这种方式在Swift中确实属于过时且适配性差的实现——一方面它依赖Objective-C运行时,和Swift现代并发模型脱节;另一方面手动调用这两个方法容易因遗漏objc_sync_exit导致死锁,容错性低。针对你提到的多变量访问但写入极少的场景(比如UI方向或应用设置更改),以下是几种更合适的现代实现方式:
1. 串行调度队列(DispatchQueue)
这是最常用的轻量级同步方案,尤其适配读写分离、写入频率低的场景。核心思路是把所有对共享变量的读写操作都提交到同一个串行队列,利用串行队列“任务依次执行”的特性保证线程安全。
实现示例:
// 定义私有串行队列,避免外部直接操作 private let syncQueue = DispatchQueue(label: "com.yourApp.settingsSyncQueue") // 共享变量 private var appTheme: String = "light" private var orientationLockEnabled: Bool = false // 读取操作(同步获取,保证拿到最新一致状态) func getCurrentSettings() -> (theme: String, lockOrientation: Bool) { var result: (String, Bool)! syncQueue.sync { result = (appTheme, orientationLockEnabled) } return result } // 写入操作(异步提交,不阻塞UI线程) func updateSettings(newTheme: String, lockOrientation: Bool) { syncQueue.async { self.appTheme = newTheme self.orientationLockEnabled = lockOrientation } }
场景适配:
因为写入操作极少,异步写入不会带来性能负担;读取用sync能立即拿到结果,同时保证读取到的是最新的一致状态。
2. NSLock/os_unfair_lock
如果需要更细粒度的锁控制,或不想依赖GCD队列,可使用原生锁。NSLock是面向对象的锁,os_unfair_lock是更底层、性能更高的锁(iOS 10+/macOS 10.12+可用)。
NSLock实现示例:
private let settingsLock = NSLock() private var appTheme: String = "light" private var orientationLockEnabled: Bool = false func getCurrentSettings() -> (String, Bool) { settingsLock.lock() defer { settingsLock.unlock() } // defer保证锁一定会释放,避免死锁 return (appTheme, orientationLockEnabled) } func updateSettings(newTheme: String, lockOrientation: Bool) { settingsLock.lock() defer { settingsLock.unlock() } appTheme = newTheme orientationLockEnabled = lockOrientation }
场景适配:
写入极少的情况下,锁的竞争概率极低,性能几乎无损耗;defer语句能避免因代码分支或异常导致的锁未释放问题,比手动调用objc_sync_exit更可靠。
3. Swift Actor(Swift 5.5+)
如果你的项目已经适配Swift并发模型(iOS 15+/macOS 12+),Actor是官方推荐的线程安全方案。Actor通过自动隔离状态,保证所有对其内部变量的访问都是串行的,无需手动管理锁或队列。
实现示例:
actor AppSettings { var appTheme: String = "light" var orientationLockEnabled: Bool = false func getCurrentSettings() -> (theme: String, lockOrientation: Bool) { return (appTheme, orientationLockEnabled) } func updateSettings(newTheme: String, lockOrientation: Bool) { appTheme = newTheme orientationLockEnabled = lockOrientation } } // 使用方式 let settings = AppSettings() // 读取(需用await,Actor方法默认异步) Task { let currentSettings = await settings.getCurrentSettings() print(currentSettings.theme) } // 写入(同样需用await) Task { await settings.updateSettings(newTheme: "dark", lockOrientation: true) }
场景适配:
Actor的自动线程安全特性完全适配你的需求,写入操作少的情况下,异步调用的开销可以忽略;同时它是Swift原生并发模型的一部分,未来兼容性更好。
方案选择建议
- 如果项目仍使用传统GCD,优先选串行队列,实现简单且兼容性好;
- 如果需要极致性能或细粒度锁控制,选os_unfair_lock;
- 如果已经适配Swift并发环境,优先选Actor,这是官方推荐的现代线程安全方案。
内容的提问来源于stack exchange,提问作者Deepak Sharma

