iOS 18 键盘必现崩溃

1,769 阅读2分钟

背景

iOS 18 beta 版本存在大量键盘相关的崩溃,崩溃堆栈如下,从堆栈可以看出这个崩溃和 webkit 相关。

1	CoreFoundation	___exceptionPreprocess()
2	libobjc.A.dylib	_objc_exception_throw()
3	Foundation	-[NSRLEArray objectAtIndex:effectiveRange:]()
4	WebKit	___70-[WKContentView(WKInteraction) setAttributedMarkedText:selectedRange:]_block_invoke()
5	WebKit	-[WKContentView(WKInteraction) setAttributedMarkedText:selectedRange:]()
6	UIKitCore	-[UIResponder(UITextInput_Internal) _setAttributedMarkedText:selectedRange:]()
7	UIKitCore	-[UIKBInputDelegateManager setAttributedMarkedText:selectedRange:]()
8	UIKitCore	-[_UIKeyboardStateManager _setAttributedMarkedText:selectedRange:inputString:lastInputString:searchString:compareAttributes:]()
9	UIKitCore	-[_UIKeyboardStateManager setMarkedText:selectedRange:inputString:lastInputString:searchString:candidateOffset:liveConversionSegments:highlighSegmentIndex:]()
10	UIKitCore	-[_UIKeyboardStateManager assertIntermediateText:]()
11	UIKitCore	-[_UIKeyboardStateManager syncKeyboardToConfiguration:]()
12	UIKitCore	___64-[_UIKeyboardStateManager handleKeyboardInput:executionContext:]_block_invoke_2()
13	UIKitCore	-[UIKeyboardTaskEntry execute:]()
14	UIKitCore	-[UIKeyboardTaskQueue continueExecutionOnMainThread]()
15	Foundation	___NSThreadPerformPerform()
16	CoreFoundation	___CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__()
17	CoreFoundation	___CFRunLoopDoSource0()
18	CoreFoundation	___CFRunLoopDoSources0()
19	CoreFoundation	___CFRunLoopRun()
20	CoreFoundation	_CFRunLoopRunSpecific()
21	GraphicsServices	_GSEventRunModal()
22	UIKitCore	-[UIApplication _run]()
23	UIKitCore	_UIApplicationMain()
24	xxx	main(0)
25	dyld	start()

崩溃分析

分析 4 WebKit ___70-[WKContentView(WKInteraction) setAttributedMarkedText:selectedRange:]_block_invoke() 处的汇编代码:

系统在键盘场景下,执行 attributesAtIndex:effectiveRange: 方法时, index(x2) 传入了 0,range(x3)传入了 null,导致必现的崩溃。等价于执行下述代码:

    NSMutableAttributedString *str = [NSMutableAttributedString new];
    [str attributesAtIndex:0 effectiveRange:NULL];

iOS 17 执行上述代码,同样是必现崩溃,因此可以确定是 WebKit 引入的低级问题。

崩溃复现

在 wk 页面弹出键盘,在键盘输入拼音,未点击确认前删除拼音,删除至最后一个拼音时,可触发该崩溃。

修复方案

我司的基建安全气垫可以兜底该崩溃。这个崩溃比较简单,并且可以稳定复现,以下也是一种可行的方案,不过本人没有在线上实践过。

崩溃是在 OC 方法里面触发的 NSException,因此 hook OC 方法添加 try catch 即可,需要注意的是,hook 需要限制号系统版本 iOS 18,另外 hook 需要注意一下是否有类簇,崩溃堆栈显示的对象是 NSRLEArray,实际调用的类是 NSMutableAttributedString。但是该崩溃可稳定复现,这里相对比较好调试。