Swift向数组追加元素触发KERN_PROTECTION_FAILURE崩溃问题求解
问题根源
你代码的核心问题是Swift 数组属于非线程安全类型,你没有对observers数组的所有读写操作做线程隔离:
- 初始版本中如果多线程同时调用
addObserver、notifyObservers、removeAllObservers,会出现并发读写竞争 - 你后续修改时只是把
append操作丢到了全局并发队列,并发队列会开启多个线程同时执行任务,依旧存在多个写操作/读写操作同时执行的问题,竞争没有被解决
崩溃诱因
KERN_PROTECTION_FAILURE是典型的非法内存访问错误,结合堆栈信息可以明确触发逻辑:
数组在执行append操作需要扩容时,会调用Array._createNewBuffer重新申请内存、修改底层缓冲区指针、调整元素引用计数,此时如果有其他线程同时操作observers数组(比如另一个线程调用append/removeAll/遍历数组),会破坏数组的内部内存结构,出现野指针访问、内存地址越界,最终触发崩溃。
修复方案
你需要保证所有对observers数组的读写操作都在同一个串行队列中执行,用串行队列的有序执行特性消除并发竞争,示例代码如下:
protocol ObservableProtocol: class { var observers: [DispatchWorkItem] { get set } func addObserver(_ block: @escaping () -> Void) func notifyObservers() func removeAllObservers() } class Observable: ObservableProtocol { internal var observers: [DispatchWorkItem] = [] // 新增私有串行队列,所有observers操作都必须在该队列执行 private let safeOperationQueue = DispatchQueue(label: "com.yourproject.Observable.safeQueue") func addObserver(_ block: @escaping () -> Void) { safeOperationQueue.async { [weak self] in guard let self = self else { return } self.observers.append(DispatchWorkItem(block: block)) } } func notifyObservers() { safeOperationQueue.async { [weak self] in guard let self = self else { return } // 先取出所有元素再执行,避免执行任务占用串行队列 let currentObservers = self.observers self.observers.removeAll() currentObservers.forEach { DispatchQueue.global().async(execute: $0) } } } func removeAllObservers() { safeOperationQueue.async { [weak self] in self?.observers.removeAll() } } deinit { // deinit里不需要加队列,此时已经没有其他线程持有该对象 observers.removeAll() } }
注意不要混用串行队列和其他线程直接操作observers数组,所有读写逻辑都必须走safeOperationQueue才能保证线程安全。
内容的提问来源于stack exchange,提问作者gvizeu
相关产品推荐
相关产品推荐

