【前言】KVO API設計很是不合理,因而有不少的KVO三方庫,好比 KVOController 用更優的API來規避這些crash,可是侵入性比較大,必須編碼規範來約束全部人都要使用該方式。有沒有什麼更優雅,無感知的接入方式?html
KVO crash 也是很是常見的 Crash 類型,在探討 KVO crash 緣由前,咱們先來看一下傳統的KVO寫發:git
#warning move this to top of .m file //#define MyKVOContext(A) static void * const A = (void*)&A; static void * const MyContext = (void*)&MyContext; #warning move this to viewdidload or init method // KVO註冊監聽: // _A 監聽 _B 的 @"keyPath" 屬性 //[self.B addObserver: self.A forKeyPath:@"keyPath" options:NSKeyValueObservingOptionNew context:MyContext]; - (void)dealloc { // KVO反註冊 [_B removeObserver:_A forKeyPath:@"keyPath"]; } // KVO監聽執行 #warning — please move this method to the class of _A - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if(context != MyContext) { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; return; } if(context == MyContext) { //if ([keyPath isEqualToString:@"keyPath"]) { id newKey = change[NSKeyValueChangeNewKey]; BOOL boolValue = [newKey boolValue]; } }
看到如上的寫發,大概咱們就明白了 API 設計不合理的地方:github
B 須要作的工做太多,B可能引發Crash的點也太多:app
B 須要主動移除監聽者的時機,不然就crash:this
Objective-C Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
KVO的被觀察者dealloc時仍然註冊着KVO致使的crash阿里雲
B 不能移除監聽者A的時機,不然就crash:編碼
添加KVO重複添加觀察者或重複移除觀察者(KVO 註冊觀察者與移除觀察者不匹配)致使的crash。spa
我有幾張阿里雲幸運券分享給你,用券購買或者升級阿里雲相應產品會有特惠驚喜哦!把想要買的產品的幸運券都領走吧!快下手,立刻就要搶光了。設計
採起的措施:code
報錯信息一覽:
2018-01-24 16:08:54.100667+0800 BootingProtection[63487:29487624] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<CYLObserverView: 0x7fb287002fb0; frame = (0 0; 207 368); layer = <CALayer: 0x604000039360>>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
因而有不少的KVO三方庫,好比 KVOController 用更優的API來規避這些crash,可是侵入性比較大,必須編碼規範來約束全部人都要使用該方式。有沒有什麼更優雅,無感知的接入方式?
那即是咱們下面要講的 KVO crash 防禦機制。