为何空实现的async版userNotificationCenter(_:didReceive:)仍会崩溃?
问题:UNUserNotificationCenterDelegate async版代理方法导致App崩溃
问题详情
- 现有带通知操作功能的App,长期使用
UNUserNotificationCenterDelegate的userNotificationCenter(_:didReceive:withCompletionHandler)方法处理通知操作,运行正常。 - 近期重构代码采用async/await,切换到无完成回调的async版代理方法
userNotificationCenter(_:didReceive:) async后,App出现大量崩溃,即使方法体仅保留打印语句或为空时仍会触发崩溃。 - 崩溃触发场景:对通知执行操作(如点击通知打开App)。
- 已提供崩溃栈信息及可复现问题的Demo项目,寻求该async版代理方法的正确使用方式以避免崩溃。
解决方案
1. 确保代理对象的生命周期稳定
async版代理方法要求代理对象在整个异步操作周期内必须存活,若代理被ARC提前回收,会直接触发野指针崩溃。
- 推荐使用全局单例作为通知代理,确保对象始终存活:
class NotificationHandler: NSObject, UNUserNotificationCenterDelegate { static let shared = NotificationHandler() private override init() { super.init() } func activate() { UNUserNotificationCenter.current().delegate = self } @available(iOS 15.0, *) func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { print("处理通知操作:\(response.actionIdentifier)") // 业务逻辑处理 } } - 在App启动时调用
NotificationHandler.shared.activate(),绑定代理后不再随意变更或释放该对象。
2. 处理线程安全问题
async版代理方法的执行队列并非默认主队列,若直接在方法内进行UI操作,会触发线程不安全崩溃,即使方法体只有打印,也可能因系统隐式的线程切换引发问题。
- 涉及UI操作时,必须切换到主队列执行:
@available(iOS 15.0, *) func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { await MainActor.run { // 在这里处理UI相关操作,如跳转页面、更新UI print("在主队列处理通知") } }
3. 兼容低iOS版本
userNotificationCenter(_:didReceive:) async是iOS 15.0及以上才支持的API,若App需要兼容更低版本,必须保留旧版的带completionHandler的代理方法作为 fallback:
// 兼容iOS <15的旧版方法 func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { // 原有处理逻辑 completionHandler() } // iOS 15+的async版本 @available(iOS 15.0, *) func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { // 新的async处理逻辑 }
系统在iOS15+设备上会优先调用async版本,低版本则自动使用旧版方法,避免因API缺失导致的异常。
4. 根据崩溃栈针对性排查
- 若崩溃栈显示
libobjc.A.dylib的objc_msgSend错误,直接排查代理对象的生命周期,确认对象未被提前释放。 - 若崩溃关联
MainThreadChecker,则聚焦线程安全问题,确保UI操作都在主队列执行。
内容的提问来源于stack exchange,提问作者gohnjanotis
相关产品推荐
相关产品推荐

