Crash问题总结与防护--Key-Value-Observing(KVO)Crash

KVO介绍

KVO(Key-Value Observing),键值监听。它提供一种机制:指定的被观察者的属性被改变后,KVO就会通知观察者,观察者可以做出响应。

KVO作用:利用KVO,很容易实现视图组件和数据模型的分离。当数据模型的属性值改变之后,作为监听者的视图组件就会被激发。这有利于业务逻辑和视图展示的解耦合。

KVO使用步骤:(1)注册观察,添加观察者及属性;(2)实现回调方法;(3)移除观察。

1
2
//注册观察者
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
1
2
//回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
1
2
3
//移除观察者
- (void)removeObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context

产生原因

(1)不能对不存在的属性进行kvo观测,否则会报Crash:

1
uncaught exception 'NSUnknownKeyException', reason: '[<SettingData 0x600000203d50> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key stockName.'

(2)观察者必须实现 observeValueForKeyPath:ofObject:change:context:方法,否则Crash。

1
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<SettingController: 0x7f811372ff70>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.

(3) 移除观察,超过addObserver的次数就会 Crash:

1
Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <SettingController 0x7ff8e8703100> for the key path "price" from <SettingData 0x60800003d000> because it is not registered as an observer.'

防护方案

 可以让被观察对象持有一个KVO的delegate,所有和KVO相关的操作均通过delegate来进行管理,delegate通过建立一张map来维护KVO整个关系。

中间层delegate的代理工作:

(1)如果出现KVO重复添加观察者或者重复移除观察者(KVO注册观察者与移除观察者不匹配)的情况,delegate可以直接阻止这些非正常的操作。

(2)被观察者dealloc之前,可以通过delegate自动将与自己有关的KVO关系都注销掉,避免了KVO的被观察者dealloc时仍然注册着KVO导致的crash。