编程iOS开发者

如何在Swift中实现线程安全的可变数据交互?描述可能在不当实现时出现的方法和问题。

用 Hintsage AI 助手通过面试

答案

在Swift中,为确保线程安全,通常使用GCD(Grand Central Dispatch)和队列(DispatchQueue)、NSLock,以及现代机制,例如actor(从Swift 5.5开始)。

基本思想 – 访问可变数据要么严格通过顺序队列,要么使用同步器(锁)。

GCD示例:

class ThreadSafeArray<Element> { private var array: [Element] = [] private let queue = DispatchQueue(label: "com.example.arrayQueue", attributes: .concurrent) func append(_ item: Element) { queue.async(flags: .barrier) { self.array.append(item) } } func get(index: Int) -> Element? { var result: Element? queue.sync { result = self.array.indices.contains(index) ? self.array[index] : nil } return result } }

现代方法:Actors (Swift 5.5+):

actor SafeCounter { private var value = 0 func increment() { value += 1 } func get() -> Int { return value } }

这两种方法都可以避免数据竞争并保持状态的一致性。

迷惑问题

问题:

如果您在主队列中使用DispatchQueue.sync,会发生什么情况,以及为什么?

答案: 会发生死锁(Deadlock),因为主队列已经在执行任务,并等待在同一队列上的同步操作完成。因此,队列无法处理下一项任务,直到当前任务完成,而这将永远不会发生。

示例:

DispatchQueue.main.sync { // 死锁:这行代码永远不会执行 }

由于对该主题细微差异了解不足而导致的实际错误示例


故事

在项目中使用了可变集合,并且没有进行同步地同时从多个线程访问。这导致应用程序崩溃和由于数据竞争造成的难以捕捉的错误:元素偶尔会丢失,有时会发生数组越界。


故事

开发者在运行时错误地在主队列上使用DispatchQueue.sync,导致死锁并导致用户界面完全停止。在更新发布后,急需修复并回滚版本。


故事

在尝试使用NSLock使类线程安全时,忘记为所有退出路径实现lock/unlock(例如,在错误或guard内部的return情况下),这导致死锁潜能以及严重的应用程序性能问题。