阅读 651

iOS 14 UIInputResponderController crash 解决

问题

7 月 8 日 iOS 14 beta 2 放出后,我们注意到一个 crash 激增了起来。

这个 crash 顶部的堆栈为:

0        _objc_retain (in libobjc.A.dylib)
1        -[UIInputResponderController prepareToMoveKeyboardForInputViewSet:animationStyle:] (in UIKitCore)
2        -[UIInputResponderController setKeyWindowSceneInputViews:animationStyle:] (in UIKitCore)
3        -[UIInputResponderController setInputViews:animationStyle:] (in UIKitCore)
4        -[UIInputResponderController setInputViews:animated:] (in UIKitCore)
5        -[UIInputResponderController setInputViews:] (in UIKitCore)
......
复制代码

并且我们注意到,这个问题的触发,和业务形态没有特别密切的联系,多个 app 都遇到了这个崩溃,且量级不低。

修复方案

先抛出一下我们最后确定的修复方案:

hook 私有方法 -[UIInputViewSet restorableResponder],直接返回 nil

由于是系统库自身的问题,我们没有源码,很难保证这个修复没有引入新的坑。但从目前的测试结果来看,至少崩溃不再复现了,并且看起来有关联的键盘场景,也没有严重问题。

原因定位

造成 crash 的原因,是系统私有类 UIInputViewSet 中的 restorableResponder 属性,既不是 weak 也不是 strong,类似于 unsafe_unretained。所以当它被访问时,很容易造成野指针。当它被赋值给一个 __strong id 类型的变量时,则会在 _objc_retain 中崩溃。

我们可以打符号断点,从汇编中确认, iOS 14 beta 2 中,restorableResponder 属性的 getter 和 setter 方法,只是存取了一个内存值,没有做任何 weak 或 strong 应有的操作。

(从汇编指令看,没有 storeWeak 或 storeStrong 操作)

可以 hook -[UIInputViewSet restorableResponder] 方法,验证一下它的返回值是不是经常是个野指针。

定位过程

是怎么定位到 -[UIInputViewSet restorableResponder] 方法的呢?我的思路是这样的:

1、猜测问题来源于最顶栈  -[UIInputResponderController prepareToMoveKeyboardForInputViewSet:animationStyle:] (in UIKitCore) 的参数

2、hook -[UIInputResponderController prepareToMoveKeyboardForInputViewSet:animationStyle:] 方法,获取传入的参数,发现有两个参数,第一个参数类型是 UIInputViewSet,这两个参数本身不是野指针。

3、hook -[UIInputResponderController prepareToMoveKeyboardForInputViewSet:animationStyle:],尝试将传入的两个参数改成 nil,发现 crash 不复现了。这基本可以确认问题出在参数上。但参数本身不是野指针,所以推测问题出现在参数某个方法的返回值上。

4、根据一份旧的 UIInputViewSet 头文件,在 lldb 中依次对这个 UIInputViewSet 对象发送消息。发现其中有个属性 restorableResponder,在旧版本上标记为 weak,但 getter 方法 return 出来的地址不是一个对象。严重怀疑这是一个野指针。

5、hook -[UIInputViewSet restorableResponder],返回 nil,crash 不复现,确认问题出在 -[UIInputViewSet restorableResponder] 方法上

6、通过汇编确认 restorableResponder 在 iOS 14 beta 2 上既不是 strong 也不是 weak

相信很多团队都遇到了这个 crash,先前在网络上搜索未果,暂且抛出我的解决方案,欢迎与大家交流。 当然最终还是等苹果爸爸真正修复它 😭