跟踪线上问题时发现一个少量的 Crash Case,错误描述为:
Termination Reason:<RBSTerminateContext| domain:10 code:0x8BADF00D explanation:scene-create watchdog transgression: application<com.kanchuan.app>:226 exhausted real (wall clock) time allowance of 13.54 seconds
Crash 线程堆栈部分如下:
Thread 0 Crashed:
0 libsystem_kernel.dylib _mach_msg_trap
1 libsystem_kernel.dylib _mach_msg
2 libdispatch.dylib __dispatch_mach_send_and_wait_for_reply
3 libdispatch.dylib _dispatch_mach_send_with_result_and_wait_for_reply$VARIANT$mp
4 libxpc.dylib _xpc_connection_send_message_with_reply_sync
5 Foundation ___NSXPCCONNECTION_IS_WAITING_FOR_A_SYNCHRONOUS_REPLY__
6 Foundation -[NSXPCConnection _sendInvocation:orArguments:count:methodSignature:selector:withProxy:]
7 Foundation -[NSXPCConnection _sendSelector:withProxy:arg1:]
8 Foundation __NSXPCDistantObjectSimpleMessageSend1
9 CoreLocation _CLGetStatusBarIconState
10 CoreLocation _CLClientIsLocationServicesEnabled
11 CoreLocation _CLClientRetrieveAuthorizationStatus
对应的源码调用入口为:
[CLLocationManager locationServicesEnabled];
由于业务的特殊要求,之前曾对 CLLocationManager的弹窗问题 做过处理,在打开 APP 后会判断系统定位服务是否开启。就这么简单的一个调用竟然能引起线上一天十几个设备的 Crash。
从已知的信息上来,这又是一起典型的因 APP 卡死超时导致被 watchdog 关闭的 Crash。Crash 集中发生在 iPhone 6s、iPhone 7 等低端设备上,且大部分都是 iOS 15.7 系统,也有一些 iOS 16 系统,表现为在打开 APP 后即 Crash。问题似乎已经很明显了,当我准备复现问题时,发现就算找到了对应的设备和系统,再加上人为营造苛刻的测试环境,都始终无法复现问题。哎,这类问题看来只能到线上放量时才能暴露出来。
xpc_connection_send_message_with_reply_sync
XPC(XPC Services)是在 iOS 和 macOS 系统中用于进程间通信(IPC)的框架,用于在不同的应用程序或进程之间进行安全且高效的数据交换。开发者在上层应用调用的一些系统 API 都需要通过 libxpc.dylib
中的 xpc_connection_send_message_with_reply_sync
方法同步从系统对应的服务中获取结果并返回,比如 UIDevice.identifierForVendor、NSUserDefaults、UIApplication.openURL等,CLLocationManager.locationServicesEnabled 内部也通过该方法向系统定位服务查询结果。
对应的还有一个 xpc_connection_send_message_with_reply
,这是一个异步方法,不会存在超时的问题。典型的,Runloop的 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
事件回调就是通过 xpc_connection_send_message_with_reply
发送 XPC 消息,用于处理 UI 事件。
问题出现的可能原因
由于始终无法复现问题,这里只能猜测原因:很可能是在 APP 启动时,在多线程环境中执行了太多需要触发 xpn reply sync 调用的方法(一个很不起眼的系统方法内部都可能调用了xpn reply sync),导致内部消息处理出现了问题,一直无法返回,直到被 watchdog 杀死。考虑到错误集中在 iOS 15.7 系统上,有理由怀疑是 iOS 15.7 引入的 bug。
如何修复?
- 检查系统启动时调用
xpc_connection_send_message_with_reply_sync
的频次,能后移的尽量后移; - 尽量避免调用能触发
xpc_connection_send_message_with_reply_sync
的接口。
比如 kanchuan 遇到的这个 locationServicesEnabled 问题,在主线程调用时,可能会收到提醒:
This method can cause UI unresponsiveness if invoked on the main thread
推荐在 CLLocationManager 的 delegate 中监听定位权限的变化(代理是由系统回调的,不会存在 xpn reply sync 问题 ),当定位状态开启后再执行业务逻辑:
- (void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager {
if (@available(iOS 14.0, *)) {
CLAuthorizationStatus status = manager.authorizationStatus;
if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse) {
}
} else {
// Fallback on earlier versions
}
}
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse) {
}
}
以上修改发布到新版本后,Crash 得到解决。
留言板