KVO探究(一)

2017-12-07  本文已影响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;

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
};

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

注意点:

总结:

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 {
    if ([key isEqualToString:@"name"]) {
        return NO;
    }
    return [super automaticallyNotifiesObserversForKey:key];
}
+ (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、写法(有两种)

- (void)setName:(NSString *)name {
   [self willChangeValueForKey:@"name"];
   _name = name;
   [self didChangeValueForKey:@"name"];
}
- (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的简单了解和简单使用,接下来将会着重研究其原理。
代码在这里

上一篇 下一篇

猜你喜欢

热点阅读