背景
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。但是该崩溃可稳定复现,这里相对比较好调试。