再探KVO

来源:互联网 发布:流程优化小组 编辑:程序博客网 时间:2024/06/05 20:03

KVO(Key-Value-Observing)键值观察 

监听property的变化 通知某些对象(观察者)关于其他对象属性值发生变化的一种机制.

优缺点

优点

性能好[开销相对于NSNotification和委托更小(只用存取方法来修改实例变量,不需要额外成本)];
容易实现视图组件和数据模型的分离,模型类的简洁;

缺点

回调方法中传递的代表变化的字典,用起来繁琐;bug难解决(会制造出人意料的代码执行路径,有一些代码运行但没有任何可见的代码说明行为发生的原因);

DEMO

// from AFNetworking--AFURLSessionManager// 添加监听对象(注册指定key路径的监听器)[task addObserver:self           forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))              options:NSKeyValueObservingOptionNew              context:NULL];// 移除监听对象(删除指定keyPath的监听器)[task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];// 回调监听- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {    if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {        if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {            self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];        }         // ...}

最佳实践

尽量保守,简单的使用KVO; 在存在复杂相互依赖关系或者复杂的类继承层次的地方避免使用.
– 9th,August,2016
注册键值依赖

// from AFNetworking+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {    if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {        return [NSSet setWithObject:@"networkReachabilityStatus"];    }    return [super keyPathsForValuesAffectingValueForKey:key];}+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {    NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];    if ([key isEqualToString:@"fullName"]) {        NSArray *affectingKeys = @[@"lastName", @"firstName"];        keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];    }    return keyPaths;}

Tips

– 5th,April,2017

添加移除位置

UIViewController中在viewWillAppear中添加键值观察,在viewWillDisappear中移除键值观察。或者init与dealloc中添加和移除键值观察。

UIView在awakeFromNib中添加键值观察,在removeFromSuperview中移除键值观察。

ps: 如果在不恰当的地方(比如UIView中在willRemoveSubview中)移除可能会导致崩溃“Cannot remove an observer because it is not registered as an observer.”,也不可在ViewDidDisappear中移除,比如在B页面的ViewDidDisappear中移除通知,页面跳转逻辑为A->B->C,B跳到C页面调用一次,B返回A页面调用一次,就会出现崩溃问题。

无法接收到观察通知:

一般就3个理由,1) 添加键值观察时对象为空;2) 通知已经发送,然后才添加的键值观察; 3)添加键值观察的对象被销毁,比如为weak或其他情况导致无法接收。

参考资料

log

5th,April,2017 – add Tips2
25th,October,2015

0 0
原创粉丝点击