問(wèn)題
7 月 8 日 iOS 14 beta 2 放出后,我們注意到一個(gè) crash 激增了起來(lái)。
這個(gè) 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)
......
并且我們注意到,這個(gè)問(wèn)題的觸發(fā),和業(yè)務(wù)形態(tài)沒(méi)有特別密切的聯(lián)系,多個(gè) app 都遇到了這個(gè)崩潰,且量級(jí)不低。
修復(fù)方案
先拋出一下我們最后確定的修復(fù)方案:
hook 私有方法 -[UIInputViewSet restorableResponder],直接返回 nil。
由于是系統(tǒng)庫(kù)自身的問(wèn)題,我們沒(méi)有源碼,很難保證這個(gè)修復(fù)沒(méi)有引入新的坑。但從目前的測(cè)試結(jié)果來(lái)看,至少崩潰不再?gòu)?fù)現(xiàn)了,并且看起來(lái)有關(guān)聯(lián)的鍵盤(pán)場(chǎng)景,也沒(méi)有嚴(yán)重問(wèn)題。
原因定位
造成 crash 的原因,是系統(tǒng)私有類 UIInputViewSet 中的 restorableResponder 屬性,既不是 weak 也不是 strong,類似于 unsafe_unretained。所以當(dāng)它被訪問(wèn)時(shí),很容易造成野指針。當(dāng)它被賦值給一個(gè) __strong id 類型的變量時(shí),則會(huì)在 _objc_retain 中崩潰。
我們可以打符號(hào)斷點(diǎn),從匯編中確認(rèn), iOS 14 beta 2 中,restorableResponder 屬性的 getter 和 setter 方法,只是存取了一個(gè)內(nèi)存值,沒(méi)有做任何 weak 或 strong 應(yīng)有的操作。


(從匯編指令看,沒(méi)有 storeWeak 或 storeStrong 操作)
可以 hook -[UIInputViewSet restorableResponder] 方法,驗(yàn)證一下它的返回值是不是經(jīng)常是個(gè)野指針。
定位過(guò)程
是怎么定位到 -[UIInputViewSet restorableResponder] 方法的呢?我的思路是這樣的:
1、猜測(cè)問(wèn)題來(lái)源于最頂棧 -[UIInputResponderController prepareToMoveKeyboardForInputViewSet:animationStyle:] (in UIKitCore) 的參數(shù)
2、hook -[UIInputResponderController prepareToMoveKeyboardForInputViewSet:animationStyle:] 方法,獲取傳入的參數(shù),發(fā)現(xiàn)有兩個(gè)參數(shù),第一個(gè)參數(shù)類型是 UIInputViewSet,這兩個(gè)參數(shù)本身不是野指針。
3、hook -[UIInputResponderController prepareToMoveKeyboardForInputViewSet:animationStyle:],嘗試將傳入的兩個(gè)參數(shù)改成 nil,發(fā)現(xiàn) crash 不復(fù)現(xiàn)了。這基本可以確認(rèn)問(wèn)題出在參數(shù)上。但參數(shù)本身不是野指針,所以推測(cè)問(wèn)題出現(xiàn)在參數(shù)某個(gè)方法的返回值上。
4、根據(jù)一份舊的 UIInputViewSet 頭文件,在 lldb 中依次對(duì)這個(gè) UIInputViewSet 對(duì)象發(fā)送消息。發(fā)現(xiàn)其中有個(gè)屬性 restorableResponder,在舊版本上標(biāo)記為 weak,但 getter 方法 return 出來(lái)的地址不是一個(gè)對(duì)象。嚴(yán)重懷疑這是一個(gè)野指針。
5、hook -[UIInputViewSet restorableResponder],返回 nil,crash 不復(fù)現(xiàn),確認(rèn)問(wèn)題出在 -[UIInputViewSet restorableResponder] 方法上
6、通過(guò)匯編確認(rèn) restorableResponder 在 iOS 14 beta 2 上既不是 strong 也不是 weak
相信很多團(tuán)隊(duì)都遇到了這個(gè) crash,先前在網(wǎng)絡(luò)上搜索未果,暫且拋出我的解決方案,歡迎與大家交流。
當(dāng)然最終還是等蘋(píng)果爸爸真正修復(fù)它 ??