再探KVO

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

你可能感兴趣的:(iOS录)