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 次反馈给了客服,又有多少用户崩溃后直接卸载转向竞品。世上无难事,只怕有心人,处理崩溃也是如此,关键是勇敢的踏出第一步。

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

相关推荐
橙子家7 小时前
浏览器缓存之【身份与会话管理】:Cookies 和 Private state tokens
前端
最新资讯动态8 小时前
HDC 2026 | 对话鲸鸿动能:存量时代,品牌如何夺回营销“主动权”?
前端
最新资讯动态8 小时前
游戏出海,从产品走向体系
前端
最新资讯动态8 小时前
20人团队跑出百万DAU、大厂也来抢量:谁在鸿蒙生态跑出加速度
前端
最新资讯动态9 小时前
千万开发者背后,鸿蒙商业化的B面
前端
爱勇宝10 小时前
AI 时代:智商决定起点,情商决定走多远
前端·ai编程
kyriewen11 小时前
用了半年 Claude Code 后,我尝试关掉它写了一周代码——结果比想象中严重
前端·javascript·ai编程
IT_陈寒11 小时前
Vite的静态资源打包让我熬夜到三点,这坑千万别跳
前端·人工智能·后端
徐小夕12 小时前
万字拆解 JitWord:企业级实时协同文档底层架构 + 大模型 AI 融合完整实践
前端·vue.js·github
一份执念12 小时前
uni-app 小程序分包限制处理与主包体积优化实战
前端·微信小程序