You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用UNUserNotificationCenter的completion handler时崩溃怎么解决?

iOS静默推送处理偶现dispatch_group_leave崩溃解决方案

根因定位

你遇到的崩溃本质是系统提供的fetchCompletionHandler被多次调用、或者遗漏调用导致的内部dispatch计数异常:
苹果要求application(_:didReceiveRemoteNotification:fetchCompletionHandler:)方法传入的回调必须且仅能被调用一次,系统内部会用dispatch_group管理该回调的生命周期:方法触发时自动调用dispatch_group_enter,你执行完逻辑调用completion时自动调用dispatch_group_leave,如果出现以下情况就会触发你看到的崩溃:

  1. 逻辑分支提前退出时没有调用completion,导致系统等待超时后自动调用,你后续逻辑又调用了一次,出现两次leave
  2. 多个逻辑分支都触发了completion调用,重复执行leave
  3. 网络请求超时、回调丢失,最终触发系统超时自动调用,后续网络回调返回后再次调用completion
  4. 子线程调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 22:45:03