访问OperationQueue的operationCount属性时应用崩溃求助
解决OperationQueue访问operationCount时的偶发崩溃问题
嘿,这个问题我之前踩过坑!咱们来一步步拆解看看👇
崩溃的核心原因
首先得明确:OperationQueue的operationCount属性并不是线程安全的。苹果的文档里虽然没明说,但队列在后台添加、执行、移除操作时,内部的操作列表是在动态变化的。如果此时你在其他线程(比如主线程)直接访问operationCount,就会触发竞态条件——两个线程同时读写队列的内部数据结构,最终导致崩溃。
你的场景里,parallelOperationQueue是后台优先级的队列,它的操作在后台线程跑,而你在parallelOperations(for:)方法里直接访问operationCount,刚好撞上队列内部修改的时机,就会出现偶发崩溃。
可行的解决方案
方案1:给operationCount加线程安全的访问包装
用一个串行DispatchQueue来保护对operationCount的访问,确保每次读取都是原子操作:
// 新增一个串行锁队列,专门保护对parallelOperationQueue的访问 private let queueAccessLock = DispatchQueue(label: "com.yourApp.operationQueueLock") private var parallelOperationQueue: OperationQueue = { let queue = OperationQueue() queue.qualityOfService = .background return queue }() // 封装一个线程安全的操作数量获取方法 private var safeOperationCount: Int { queueAccessLock.sync { parallelOperationQueue.operationCount } } // 修改你的方法,用safeOperationCount代替直接访问 private func parallelOperations(for tokens: [TokenObject]) { guard safeOperationCount == 0, !tokens.isEmpty else { return } // 后续逻辑... }
方案2:自己维护线程安全的操作计数器(更可靠)
如果你的业务逻辑依赖队列是否为空,其实完全可以自己跟踪操作的生命周期,避免依赖系统提供的operationCount。这样不仅线程安全,还能避免系统属性的不确定性:
private let countLock = NSLock() private var activeOperationCount = 0 private var parallelOperationQueue: OperationQueue = { let queue = OperationQueue() queue.qualityOfService = .background return queue }() // 封装添加操作的方法,自动维护计数器 private func addSafeOperation(_ operation: Operation) { countLock.lock() activeOperationCount += 1 countLock.unlock() // 给操作添加完成回调,计数器减1 operation.completionBlock = { [weak self] in guard let self = self else { return } self.countLock.lock() self.activeOperationCount -= 1 self.countLock.unlock() } parallelOperationQueue.addOperation(operation) } // 修改你的判断逻辑 private func parallelOperations(for tokens: [TokenObject]) { countLock.lock() let isQueueIdle = activeOperationCount == 0 countLock.unlock() guard isQueueIdle, !tokens.isEmpty else { return } // 后续逻辑... }
方案3:用DispatchGroup跟踪操作完成(适合批量操作场景)
如果你的场景是要等一批操作全部完成后再执行新任务,用DispatchGroup会更直观:
private let operationGroup = DispatchGroup() private var parallelOperationQueue: OperationQueue = { let queue = OperationQueue() queue.qualityOfService = .background return queue }() // 添加操作时进入group private func addGroupOperation(_ operation: Operation) { operationGroup.enter() operation.completionBlock = { self.operationGroup.leave() } parallelOperationQueue.addOperation(operation) } // 等待所有操作完成(注意:不要在主线程调用wait方法!) private func waitForAllOperations() { operationGroup.wait() // 所有操作完成后的逻辑 }
额外提醒
尽量不要依赖operationCount做业务判断,因为它的返回值是瞬时状态——哪怕你刚读到0,下一秒可能就有其他线程往队列里加了新操作。自己维护状态或者用DispatchGroup、Operation依赖,会让逻辑更可靠。
内容的提问来源于stack exchange,提问作者Oleg Gordiichuk
相关产品推荐
相关产品推荐

