iOS应用接收远程通知崩溃:Array._getElement问题排查与修复
排查与修复:FitSW App Store应用远程通知触发崩溃
问题场景
我负责的FitSW应用已经上线App Store,最近收到Xcode上报的崩溃日志,崩溃发生在应用接收远程通知的时候。日志里的关键栈帧指向:
Array._getElement(_:wasNativeTypeChecked:matchingSubscriptCheck:) + 12 (AppDelegate.swift:0)
一开始我完全找不到这个私有函数的相关代码,检查AppDelegate里的通知处理逻辑也没发现明显问题,而且不管怎么用模拟器测试,都没法复现这个崩溃。
触发崩溃的核心代码
崩溃对应的是application(_:didReceiveRemoteNotification:fetchCompletionHandler:)方法,原来的代码大概是这样:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { let notificationData = userInfo["data"] as? [String] ?? [] let targetItem = notificationData[0] // 后续处理通知内容的逻辑 completionHandler(.newData) }
排查思路
- 解析崩溃栈:
Array._getElement是Swift数组的私有底层函数,出现这个栈帧基本可以确定是数组越界访问导致的崩溃。 - 复现困境:模拟器测试时,我们发送的测试通知payload里
data字段都是非空数组,所以没法触发;但生产环境中,部分通知的data字段是空数组,这时候直接访问[0]就会触发崩溃。 - 线程排查:另外发现通知回调可能在非主线程执行,某些情况下UI相关的操作会间接影响数组访问的稳定性。
最终修复方案
针对这两个问题,我做了两处修改:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { // 延迟到主线程执行,避免非主线程的潜在问题 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in // 严格校验数组是否存在且非空 guard let notificationData = userInfo["data"] as? [String], !notificationData.isEmpty else { completionHandler(.noData) return } let targetItem = notificationData[0] // 后续处理通知内容的逻辑 completionHandler(.newData) } }
- 数组越界防护:用
guard语句确保notificationData是有效数组且不为空,从根源避免越界访问。 - 线程调度优化:通过
DispatchQueue.main.asyncAfter延迟执行通知处理逻辑,保证在主线程且合适的时机执行,解决非主线程带来的潜在稳定性问题。
内容的提问来源于stack exchange,提问作者cmccune
相关产品推荐
相关产品推荐

