KVO是观察者设计模式在Foundation框架中的应用。
1,相关API
苹果给NSObject提供了一个分类,提供了三个方法
@interface NSObject(NSKeyValueObserverRegistration)
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
@end
该分类提供了一个添加观察者和两个移除观察者的方法,移除观察者的两个方法中一个带有上下文参数,一个不带,苹果建议使用带有上下文参数的api,因为该方法可以精确的指定要移除的对象,如果不带上下文参数的话还需要系系统去推测,当一个对象重复注册同一个keypath的时候有可能推断错误
如果想观察某个对象的属性的变化,可以先添加观察者,实现代理方法,然后在适当的时候移除观察者即可。 当添加完观察者后,如果观察的属性发成变化会调用观察者的下面的方法
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;
注意: 当一个对象添加了多个keypath观察的时候,无论哪一个属性发生变化都会走这个方法, 好在方法参数带有发生变化时的值,对象和上下文, 可以据此加以区分
2,实现原理
kvo的底层实现用到了运行时,当向一个对象的某个属性添加观察者时,系统会利用运行时动态创建一个该类的子类,并且在子类中重写该属性的set方法, 然后在子类重写的方法中, 先调用即将赋值的方法,然后调用赋值方法,然后再调用已经赋值完成的方法。这样当对象的属性发展变化的时候就能及时的通知观察者。
如果移除了属性的观察者, 那么子类中属性的重写方法也会被移除。
如果移除了一个对象的所有观察者,那么其子类也会自动被移除
代码验证可以看参考http://blog.sunnyxx.com/2014/03/09/objc_kvo_secret/
3,作用
个人感觉如果需要持续性间断的获取某个值的时候可以考虑使用kvo, 例如检测下载进度,网页的加载进度和图片的加载进度。最近我就需要 了一个需求需要动态获取网页的内容高度, 网页比较简单,就是加载文字和图片。因为加载过程需要一定的时间,一次性获得的高度有可能不对,这时候布局的话就是不正常的。
4, 注意点
1,当父类和子类都添加属性观察时,无论是父类还是子类观察的属性发生变化的时候都会调用子类的回调方法,所以在子类的回调方法中要注意判断,不能阻断了父类的响应链
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if (object == self.view) {
// 判断如果是子类监听的属性 做操作 否则调用父类方法,将响应率交给父类
}
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
可以在添加监听的时候指定唯一的content 然后在回调方法中就可以判断然后做不同的处理
[self.webView addObserver:self forKeyPath: @"frame" options:NSKeyValueObservingOptionNew context:@"asdfasdf"];
这样添加监听后就可以在回调方法中根据标识 asdfasdf 来唯一确定
2,要避免同一个对象对同一个path的多次监听,否则会引发崩溃。
3,及时安全的移除监听