iOS中Dispatch Group是否阻塞主线程?CallKit扩展UI卡顿排查
CallKit扩展查询导致程序挂起、UI卡顿问题排查
问题描述
调用代码查询并更新CallKit通话扩展时,偶尔出现程序挂起、通话超时情况。原本以为将代码移至主线程外就不会阻塞UI,但实际UI仍出现卡顿,不确定是否由以下代码导致:
查询函数代码
private func queryCallExtensionStatusWithDispatchGroup() { let dg = DispatchGroup() dg.enter() self.doQueryCallExtensionStatusWithDispatchGroup(dispatchGroup: dg) Logger.trace(TAG + "queryCallExtensionStatusWithDispatchGroup() WAITING .... ") let dispatchTimeoutResult = dg.wait(timeout: DispatchTime.now() + DispatchTimeInterval.seconds(30)) if dispatchTimeoutResult == .timedOut { Logger.info(TAG + "queryCallExtensionStatusWithDispatchGroup() TIMED OUT") } else { Logger.trace(TAG + "queryCallExtensionStatusWithDispatchGroup() LEFT") } } private func doQueryCallExtensionStatusWithDispatchGroup(dispatchGroup:DispatchGroup) { <snip> dispatchGroup.leave() }
调用代码
private let callExtensionQueue = DispatchQueue(label: "com.appname.serialQueue") ... callExtensionQueue.async { self!.queryCallExtensionStatusWithDispatchGroup() }
问题根源与修复建议
核心问题
你虽然将查询逻辑放到了自定义串行队列,但queryCallExtensionStatusWithDispatchGroup中调用的dg.wait()会阻塞当前队列的线程:
- 如果
doQueryCallExtensionStatusWithDispatchGroup内部存在需要主线程配合的操作(比如间接依赖主线程资源、或错误地在该队列执行UI更新),会导致主线程被间接阻塞,引发UI卡顿。 - 若
dispatchGroup.leave()未在所有分支被正确调用(比如异步逻辑异常),wait()会阻塞队列线程直到超时,期间该队列的后续任务全部无法执行,可能引发程序挂起。
修复方案
- 替换同步等待为异步通知,彻底避免阻塞队列线程:
private func queryCallExtensionStatus() { let dg = DispatchGroup() dg.enter() self.doQueryCallExtensionStatus(dispatchGroup: dg) Logger.trace(TAG + "queryCallExtensionStatus() waiting asynchronously .... ") // 监听查询完成事件 dg.notify(queue: callExtensionQueue) { Logger.trace(TAG + "queryCallExtensionStatus() completed") } // 实现超时逻辑 let timeoutWorkItem = DispatchWorkItem { Logger.info(TAG + "queryCallExtensionStatus() TIMED OUT") } DispatchQueue.global().asyncAfter(deadline: .now() + 30, execute: timeoutWorkItem) // 查询完成时取消超时任务 dg.notify(queue: .global()) { timeoutWorkItem.cancel() } } private func doQueryCallExtensionStatus(dispatchGroup: DispatchGroup) { // 原有业务逻辑... dispatchGroup.leave() } - 检查
doQueryCallExtensionStatusWithDispatchGroup内部逻辑:- 所有UI相关操作必须通过
DispatchQueue.main.async切换到主线程执行。 - 确保
dispatchGroup.leave()在所有代码分支(包括错误、异常分支)都能被调用,避免DispatchGroup一直处于等待状态。
- 所有UI相关操作必须通过
- 优先使用CallKit原生异步API:如果CallKit查询操作本身提供回调接口,直接用回调替代手动构建DispatchGroup的同步等待逻辑。
内容的提问来源于stack exchange,提问作者GruntBiscuits
相关产品推荐
相关产品推荐

