调用UNUserNotificationCenter的completion handler时崩溃怎么解决?
iOS静默推送处理偶现dispatch_group_leave崩溃解决方案
根因定位
你遇到的崩溃本质是系统提供的fetchCompletionHandler被多次调用、或者遗漏调用导致的内部dispatch计数异常:
苹果要求application(_:didReceiveRemoteNotification:fetchCompletionHandler:)方法传入的回调必须且仅能被调用一次,系统内部会用dispatch_group管理该回调的生命周期:方法触发时自动调用dispatch_group_enter,你执行完逻辑调用completion时自动调用dispatch_group_leave,如果出现以下情况就会触发你看到的崩溃:
- 逻辑分支提前退出时没有调用completion,导致系统等待超时后自动调用,你后续逻辑又调用了一次,出现两次leave
- 多个逻辑分支都触发了completion调用,重复执行leave
- 网络请求超时、回调丢失,最终触发系统超时自动调用,后续网络回调返回后再次调用completion
- 子线程调用completion触发线程安全问题
修复方案
1. 补全所有提前退出分支的completion调用
你当前代码前三个guard校验失败直接return,没有调用completion,需要修改:
guard let ship: String = payload["ship"] as? String else { completionHandler(.noData) return } guard let node: String = payload["node"] as? String else { completionHandler(.noData) return } guard let graph: String = payload["graph"] as? String else { completionHandler(.noData) return }
2. 给completion加调用保护,避免重复执行
在方法开头定义一个安全的回调包裹器,确保只会被调用一次,且统一切到主线程执行:
// 标记是否已经调用过回调 var hasInvokedCompletion = false let safeCompletion: (UIBackgroundFetchResult) -> Void = { result in guard !hasInvokedCompletion else { return } hasInvokedCompletion = true // 系统要求回调必须在主线程调用,避免线程异常 DispatchQueue.main.async { completionHandler(result) } }
后续所有需要调用原completionHandler的位置,全部替换为调用safeCompletion。
3. 增加超时保护
系统给静默推送的后台处理时间最多30秒,超过会强制终止进程或自动调用回调,建议加25秒超时兜底:
// 方法开头加超时逻辑 DispatchQueue.main.asyncAfter(deadline: .now() + 25) { safeCompletion(.failed) }
4. 补全网络请求的异常分支处理
检查scryForGroupInfo、scryOnNotificationReceipt两个网络方法的实现,确保请求失败、超时的时候都会触发回调,回调失败时直接调用safeCompletion(.failed)即可。
内容的提问来源于stack exchange,提问作者narner
相关产品推荐
相关产品推荐

