iOS KVO crash 自修復技術實現與原理解析
摘要: KVO API設計非常不合理,於是有很多的KVO三方庫,比如 KVOController 用更優的API來規避這些crash,但是侵入性比較大,必須編碼規範來約束所有人都要使用該方式。有沒有什麼更優雅,無感知的接入方式?
KVO crash 自修復技術實現與原理解析
前言
【前言】KVO API設計非常不合理,於是有很多的KVO三方庫,比如 KVOController 用更優的API來規避這些crash,但是侵入性比較大,必須編碼規範來約束所有人都要使用該方式。有沒有什麼更優雅,無感知的接入方式?
簡介
KVO crash 也是非常常見的 Crash 類型,在探討 KVO crash 原因前,我們先來看一下傳統的KVO寫發:
看到如上的寫發,大概我們就明白了 API 設計不合理的地方:
B 需要做的工作太多,B可能引起Crash的點也太多:
B 需要主動移除監聽者的時機,否則就crash:
- B 在釋放變為nil後,hook dealloc時機
- A 在釋放變為nil後 否則報錯
Objective-C Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
KVO的被觀察者dealloc時仍然註冊著KVO導致的crash
B 不能移除監聽者A的時機,否則就crash:
- B沒有被A監聽
- B已經移除A的監聽。
添加KVO重複添加觀察者或重複移除觀察者(KVO 註冊觀察者與移除觀察者不匹配)導致的crash。
採取的措施:
- B添加A監聽的時候,避免重複添加,移除的時候避免重複移除。
- B dealloc時及時移除 A
- A dealloc時,讓 B 移除A。
- 避免重複添加,避免重複移除。
報錯信息一覽:
防crash措施
於是有很多的KVO三方庫,比如 KVOController 用更優的API來規避這些crash,但是侵入性比較大,必須編碼規範來約束所有人都要使用該方式。有沒有什麼更優雅,無感知的接入方式?
那便是我們下面要講的 KVO crash 防護機制。
我們可以對比下其他的一些KVO防護方案:
網路上有一些類似的方案,「大白健康系統」方案大致如下:
KVO的被觀察者dealloc時仍然註冊著KVO導致的crash 的情況,可以將NSObject的dealloc swizzle, 在object dealloc的時候自動將其對應的kvodelegate所有和kvo相關的數據清空,然後將kvodelegate也置空。避免出現KVO的被觀察者dealloc時仍然註冊著KVO而產生的crash
這樣未免太過麻煩,我們可以藉助第三方庫 CYLDeallocBlockExecutor hook 任意一個對象的 dealloc 時機,然後在 dealloc 前進行我們需要進行的操作,因此也就不需要為 NSObject 加 flag 來進行全局的篩選。flag 效率非常底,影響 app 性能。
「大白健康系統」思路是建立一個delegate,觀察者和被觀察者通過delegate間接建立聯繫,由於沒有demo源碼,這種方案比較繁瑣。可以考慮建立一個哈希表,用來保存觀察者、keyPath的信息,如果哈希表裡已經有了相關的觀察者,keyPath信息,那麼繼續添加觀察者的話,就不載進行添加,同樣移除觀察的時候,也現在哈希表中進行查找,如果存在觀察者,keypath信息,那麼移除,如果沒有的話就不執行相關的移除操作。要實現這樣的思路就需要用到methodSwizzle來進行方法交換。我這通過寫了一個NSObject的cagegory來進行方法交換。示例代碼如下:
下面是核心的swizzle方法:
之後我們就可以模擬dealloc中不寫removeObserver
,同時也可以寫,
同時也可以多次 addObserver
、removeObserver
這樣就完全不干擾我們平時的代碼書寫邏輯了。
更多技術乾貨敬請關注云棲社區知乎機構號:阿里云云棲社區 - 知乎
推薦閱讀:
※從實際體驗來看,M7 處理器是否為 iPhone 5s 的續航帶來了顯著提升?
※小碼哥教育培訓怎麼樣?
※在Mac上做開發是否一定要用Retina屏?
※最終幻想8 Final Fantasy Ⅷ為什麼沒有移植到iOS上?
※為什麼蘋果不採用託管語言?
TAG:iOS | ObjectiveC | 編碼 |