iOS開源:Dr.Light - 輕量 iOS crash 保護方案

VerKhull 7年前發布 | 7K 次閱讀 iOS開發 移動開發

前一段時間看了網易大白健康管理系統的文章,感覺很不錯,文中對iOS app常見的幾種crash進行了總結和分析,并提出了修復方案。平時開發過程中,筆者也略有積累,對一些潛在的crash也有自己的處理方案。利用業余時間開發完成一個簡單輕易的開源庫,專門處理幾種常見的crash,雖還不完善,秉承回饋開源社區,為大家做下分享。

項目命名為Dr.Light,靈感來自于去年看的漫威電影《驚奇博士》。目前修復了以下幾種情況:

UI 非主線程刷新

如果UI的刷新沒有在主線程發生,導致的后果包括可能會有crash、動畫失效、界面無反應或者異常等。

解決方案:利用runtime,hook了view的 setNeedsLayout 、 setNeedsDisplay 、 setNeedsDisplayInRect: 、 setNeedsUpdateConstraints 四個方法,判斷當前是否是主隊列,如果不是主隊列,則跳轉到 dispatch_get_main_queue 執行。

KVO 非對稱添加刪除

我個人在響應式編程中偏愛使用KVO,它帶來的益處不言自明。如果因為疏忽導致add observer/remove obsever不成對出現,可能會導致crash。

解決方案:hook了NSObject的 addObserver:forKeyPath:options:context: 、 removeObserver:forKeyPath: 。為被觀察對象增加一個map屬性,存儲所有的observer和keypath的對應關系。每次添加或者刪除觀察者的時候,都會先去檢查observer的keypath是否已存在或者消失,在執行相當邏輯。避免重復添加或者刪除。

下面展示添加觀察者的簡單流程圖,刪除類似。

注意:使用前需要設置被觀察者的 kvoSafteyToggle 為true,原因是因為許多系統操作會執行kvo操作,沒必要為系統kvo增加保護,浪費內存。 非死book的開源庫 kvocontroller 可以解決此問題,還能保證線程安全,相比其本項目更輕,代碼量更少,有興趣的可以看下。

完善:被觀察者銷毀的時候,是否還有殘留觀察者,這個項目沒有加。如果做的話方案是hook dealloc 加判斷。

navigationcontroller的連續push

先說一個常見的例子,比如點擊某個按鈕push到一個新的界面,如果連續快速點擊,界面就會發生連續的push,這種情景下不一定會發生crash。我見過網上有一種解決方案,也是利用運行時hook了button的sendaction事件,增加時間戳或者計數器,來阻止短時間點擊事件的連續調用。個人覺得這個方案雖然能解決問題,但只是浮于表面,甚至可以說是本末倒置。button并沒有做錯什么卻要“背鍋”,應該讓真正的”肇事者“navigation去處理。

button action ----->  push viewcontroller

crash的情景,在viewcontroller的 viewDidLoad 執行pushViewController方法,由于此時界面布局尚未完成,會發生 nested push 崩潰,用umeng或者bugly會收集到 cannot addsubView:self 錯誤信息。

解決方案:hook UINavigationController 的 pushViewController:animated: 方法,設置一個時間間隔,記錄每次push的時間,如果距離上次push時間間隔較短,則禁止push操作。

unrecognized selector

未知選擇器,具體原因和系統的消息轉發機制有關,這里不在專門講述。

解決方案:hook NSObject的 methodSignatureForSelector: 和 forwardInvocation: 。創建一個單例對象,在 forwardInvocation: 中將消息轉交該單例處理。利用runtime為單例中增加一個方法,增加一個目標selector的新指向IMP(具體實現為空),這樣單例就能像安全地全盤接收無效selector。

開始的時候打算把消息的攔截處理放在 forwardingTargetForSelector 中,畢竟這里提供轉發對象就可以了,而且這個函數的調用時機比上面兩個都早,理論上越早攔截無效操作越好。不過開發過程中,我發現許多系統級函數例如 getServerAnswerForQuestion:reply: 居然也會出現在 forwardingTargetForSelector 里,這讓我有些意外。無奈之下,只能將攔截操作下沉。

總結

還有幾種錯誤,比如數組越界,空指針插入dictionary。這種情景跟業務牽扯較大,即使加了處理,但后續界面展現也可能造成困擾,建議開發者在開發階段就增加判斷。

NSString *str  = [@"Tom Hanks" substringFromIndex:10];
    _label.text = str;

 

 

 本文由用戶 VerKhull 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!