iOS 17 CKContextContentProviderUIScene 崩溃处理

背景

iOS 17 发布后,我司的 App 新增了 n 个崩溃,其中的一个崩溃堆栈如下所示:

scss 复制代码
0	libobjc.A.dylib	_objc_retain()
1	ContextKitExtraction	+[CKContextContentProviderUIScene _bestVisibleStringForView:usingExecutor:]()
2	ContextKitExtraction	+[CKContextContentProviderUIScene _donateContentsOfWindow:usingExecutor:withOptions:]()
3	ContextKitExtraction	___78+[CKContextContentProviderUIScene extractFromScene:usingExecutor:withOptions:]_block_invoke()
4	ContextKitExtraction	___64-[CKContextExecutor addWorkItemToQueue:withWorkItem:andContext:]_block_invoke()
5	libdispatch.dylib	__dispatch_call_block_and_release()
6	libdispatch.dylib	__dispatch_client_callout()
7	libdispatch.dylib	__dispatch_main_queue_drain()
8	libdispatch.dylib	__dispatch_main_queue_callback_4CF()
9	CoreFoundation	___CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__()
10	CoreFoundation	___CFRunLoopRun()
11	CoreFoundation	_CFRunLoopRunSpecific()
12	GraphicsServices	_GSEventRunModal()
13	UIKitCore	-[UIApplication _run]()
14	UIKitCore	_UIApplicationMain()
15 xxxx  	main(0)
16	dyld	start()

崩溃发生在 ContextKitExtraction 里面,不包含任何业务相关的堆栈,所以这个崩溃在之前被误判为 iOS 17 的系统问题。另外崩溃的量级并不多,单日崩溃用户峰值 1000 左右,排查的优先级并不高,也没有分配人力去跟进。

直到有一天,线上用户反馈连续崩溃了 9 次,查看该用户的日志上报,发现罪魁祸首就是这个崩溃。

排查过程

通过分析用户的行为日志,我们找到了高概率复现的操作路径。说实话,能够复现的崩溃都不是什么难题,但是这个崩溃踩的坑比较奇怪,所以写到这里和大家分享下。

Xcode debug 复现崩溃:

查看崩溃栈帧 _bestVisibleStringForView 上下文的汇编代码:

<+1488> 处: bl 0x1be14cb20 判断对象是否响应某个方法:

yaml 复制代码
->  0x1be14cb20: adrp   x16, -242784
    0x1be14cb24: add    x16, x16, #0x84           ; objc_opt_respondsToSelector
    0x1be14cb28: br     x16
    0x1be14cb2c: brk    #0x1
    0x1be14cb30: adrp   x16, -242842
    0x1be14cb34: add    x16, x16, #0x70c          ; objc_release
    0x1be14cb38: br     x16
    0x1be14cb3c: brk    #0x1

<+1492> 处 0x1b88cbfa0 <+1492>: tbz w0, #0x0, 0x1b88cc014 ; <+1608> respondsToSelector 如果返回 false 跳转到 0x1b88cc014 处,否则继续执行。

<+1504> 执行该方法:

bash 复制代码
0x1b88cbfa4 <+1496>: mov    x0, x19
0x1b88cbfa8 <+1500>: mov    x2, x23
0x1b88cbfac <+1504>: bl     0x1b88da460               ; objc_msgSend$performSelector:

打印 x19 的值,x19 是执行 performSelector 时的 self,值是一个自定义视图对象:

<PickerColumnView: 0x17dcdcca0; frame = (195 0; 195 257); backgroundColor = UIExtendedGrayColorSpace 0 0; layer = <CALayer: 0x30107df60>>

打印 x23 的值,x23 是执行 performSelector 时的 selector:

(lldb) po (char *)$x23 "component"

查看 PickerColumnView 的头文件,发生声明了属性 component, 不过类型是基础类型 NSUInteger。

objectivec 复制代码
@property (nonatomic, assign) NSUInteger component;     // 当前纵列

<+1508> bl 0x1be14ca70 执行 objc_retain 触发崩溃。

排查到这里,根据目前已有的信息,崩溃的原因就比较明确了。

结论

+[CKContextContentProviderUIScene _bestVisibleStringForView:usingExecutor:] 方法内的处理逻辑为:

  1. 使用 respondsToSelector 判断视图是否是否响应 component 方法
  2. 是则通过调用 performSelector 执行 component 方法。
  3. 对 component 方法返回值,执行 objc_retain。

PickerColumnView 视图,存在 component 方法,但是返回值是 NSUInteger 类型,对基础类型执行 objc_retain 时,触发崩溃。

反思

这个崩溃堆栈看起来唬人,但是着手排查后才发现是只纸老虎,前后排查过程大概耗时两个小时,但是崩溃一开始出现时没有及时的去解决,导致这个实际简单的问题在线上持续存在了几个月,一个用户崩了 9 次反馈给了客服,又有多少用户崩溃后直接卸载转向竞品。世上无难事,只怕有心人,处理崩溃也是如此,关键是勇敢的踏出第一步。

另外,在崩溃时如果能获取足够多的信息,在解决问题时就会有更多的筹码,以这个崩溃为例,如果能在线上崩溃时回溯出栈帧上的寄存器信息并上报,也不会被误判为一个单纯的系统问题,所以笔者最近也开始着手调研实现崩溃栈帧信息的回溯。感兴趣的老板可以点点关注,敬请期待后续的更新。

相关推荐
耶啵奶膘1 小时前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^3 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie3 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic4 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿4 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具5 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
qq_390161775 小时前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test6 小时前
js下载excel示例demo
前端·javascript·excel
Yaml46 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事6 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro