美文网首页
KVO探究(一)

KVO探究(一)

作者: 大鹏鸟 | 来源:发表于2017-12-07 15:50 被阅读75次

    KVO的调用分为自动调用和手动调用,一般的使用自动调用比较多。下面先说说自动调用。

    一、自动调用

    准备工作:

    1、定义一个model,代码如下:

    @interface ZPZPersonModel : NSObject
    
    @property (nonatomic, copy) NSString * name;
    @property (nonatomic, assign) NSInteger age;
    @property (nonatomic, copy) NSString * ID;
    @property (nonatomic, assign) CGFloat height;
    
    @end
    

    2、在vc中添加观察者

    - (void)addKVOForModel {
        _personModel = [[ZPZPersonModel alloc] init];
        [_personModel addObserver:self forKeyPath:NSStringFromSelector(@selector(name)) options:NSKeyValueObservingOptionNew context:&personContext];
        [_personModel addObserver:self forKeyPath:NSStringFromSelector(@selector(age)) options:NSKeyValueObservingOptionOld context:&personContext];
        [_personModel addObserver:self forKeyPath:NSStringFromSelector(@selector(ID)) options:NSKeyValueObservingOptionInitial context:&personContext];
        [_personModel addObserver:self forKeyPath:NSStringFromSelector(@selector(height)) options:NSKeyValueObservingOptionPrior context:&personContext];
        NSLog(@"before:%p",_personModel);
    }
    

    3、回调

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        NSLog(@"%@--%@",keyPath,change);
    }
    

    1、参数的了解

    使用如下代码添加观察者:

    - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
    
    • observer:谁来观察该对象的属性和方法
    • keyPath:要观察的对象的属性和方法,一般使用NSStringFromSelector(@selector(selector))来获取
    • options:决定观察的类型,不同的类型,触发的回调的内容和时机不同,后面会着重说
    • context:随机值

    options

    typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {
    NSKeyValueObservingOptionNew = 0x01,
    NSKeyValueObservingOptionOld = 0x02,
    NSKeyValueObservingOptionInitial API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) = 0x04,
    NSKeyValueObservingOptionPrior API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) = 0x08
    };

    使用方法:可以任意多个组合,比如任意两个、三个、四个组合

    注意点:

    • NSKeyValueObservingOptionNew:在添加了观察者后并赋值了会有回调,返回NSKeyValueChangeKindKey和NSKeyValueChangeNewKey
    • NSKeyValueObservingOptionOld:在添加了观察者后并赋值了会有回调,返回NSKeyValueChangeKindKey和NSKeyValueChangeOldKey
    • NSKeyValueObservingOptionInitial:添加观察者时就触发回调,并且在后面赋值时也会触发回调,但是都只返回NSKeyValueChangeKindKey
    • NSKeyValueObservingOptionPrior:在添加了观察者后并赋值了会有回调,但是会回调两次,第一次返回NSKeyValueChangeKindKey、NSKeyValueChangeNotificationIsPriorKey,第二次回调只返回NSKeyValueChangeKindKey
    • 组合调用1:NSKeyValueObservingOptionInitial只有在和NSKeyValueObservingOptionNew搭配的时候,才会返回NSKeyValueChangeNewKey,和其他的搭配都只返回NSKeyValueChangeKindKey
    • 组合调用2:NSKeyValueObservingOptionPrior和NSKeyValueObservingOptionNew或者NSKeyValueObservingOptionOld搭配时,只会在第二次回调时返回NSKeyValueChangeNewKey或者NSKeyValueChangeOldKey

    总结:

    1、从上面可以看出,只有options为NSKeyValueObservingOptionNew和NSKeyValueObservingOptionOld会有值的输出
    2、同一个对象的keyPath不要添加多次,否则会执行多次,如下面的代码就会执行两次,调用顺序是按照先进后出的顺序:

     [_personModel addObserver:self forKeyPath:NSStringFromSelector(@selector(ID)) options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial context:&personContext];
     [_personModel addObserver:self forKeyPath:NSStringFromSelector(@selector(ID)) options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial context:&personContext];
    

    2、正常对象调用

    上面的例子就是,这里不再赘述

    3、NSArray和NSSet添加观察者(看后面

    二、手动调用

    这里以给对象_personModel的属性name添加观察者。

    1、必须要在监控的对象里重写一些方法(有两种重写方式)

    • 直接重写+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key方法
     + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
        if ([key isEqualToString:@"name"]) {
            return NO;
        }
        return [super automaticallyNotifiesObserversForKey:key];
    }
    
    • 每个属性会生成相应的可用来设置是否手动触发观察者的方法,只限于属性,方法是不会生成的,比如name会生成+ (BOOL)automaticallyNotifiesObserversOfName方法:
    + (BOOL)automaticallyNotifiesObserversOfName {
        return NO;
    }
    
    如果上述两个方法都出现了,则第一个的优先级高于第二个,如下所示:
    + (BOOL)automaticallyNotifiesObserversOfName {
        return NO;
    }
    
    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
        if ([key isEqualToString:@"name"] || [key isEqualToString:@"ID"]) {
            return NO;
        }
        return [super automaticallyNotifiesObserversForKey:key];
    }
    

    不会再去调用第一个方法!

    2、写法(有两种)

    • 第一种:直接写在属性的set方法里,如下:
    - (void)setName:(NSString *)name {
       [self willChangeValueForKey:@"name"];
       _name = name;
       [self didChangeValueForKey:@"name"];
    }
    
    • 第二种:写在要改变值的地方(这里以ID为观察对象),如下:
    - (void)givePersonName {
        _personModel.name = @"new name";
        [_personModel willChangeValueForKey:@"ID"];
        _personModel.ID = @"999";
        [_personModel didChangeValueForKey:@"ID"];
        [_personModel addName:@"zhou" andID:@"999"];
    }
    

    3、添加观察者
    和自动调用一致,添加都是一样的:

    - (void)addKVOForModel {
        _personModel = [[ZPZPersonModel alloc] init];
        [_personModel addObserver:self forKeyPath:NSStringFromSelector(@selector(name)) options:NSKeyValueObservingOptionNew context:NULL];
        [_personModel addObserver:self forKeyPath:NSStringFromSelector(@selector(ID)) options:NSKeyValueObservingOptionNew context:NULL];
        [_personModel addObserver:self forKeyPath:NSStringFromSelector(@selector(addName:andID:)) options:NSKeyValueObservingOptionNew context:NULL];
    }
    

    既然是Key-Value观察,那么对方法是不起作用的!!!

    三、总结

    1、通过断点会发现,观察者对于同一个属性的添加只会添加一次,即下面的代码只会调用一次,这是为什么?

    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
        if ([key isEqualToString:@"name"] || [key isEqualToString:@"ID"]) {
            return NO;
        }
        BOOL result = [super automaticallyNotifiesObserversForKey:key];
        return result;
    }
    

    2、方法+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key 在调用 [_personModel addObserver:self forKeyPath:NSStringFromSelector(@selector(name)) options:NSKeyValueObservingOptionNew context:NULL];时调用,可以猜测,在该方法里自动调用了+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key。

    以上只是对KVO的简单了解和简单使用,接下来将会着重研究其原理。
    代码在这里

    相关文章

      网友评论

          本文标题:KVO探究(一)

          本文链接:https://www.haomeiwen.com/subject/mjhdixtx.html