【前言】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 防御机制。