美文网首页
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探究(一)

    KVO的调用分为自动调用和手动调用,一般的使用自动调用比较多。下面先说说自动调用。 一、自动调用 准备工作: 1、...

  • swift中KVO和属性观察器

    开篇提醒:OC中的KVO及其KVO的基础知识可参见:深入runtime探究KVO Swift中,原本没有KVO模式...

  • KVO探究

    KVO原理 KVO是基于runtime机制实现的当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的...

  • KVO探究

    1. 介绍 键值观察是一种机制,它允许将其他对象的指定属性的更改通知给对象。 对于MVC中model层和contr...

  • KVO 探究(一)自己实现KVO

    何为KVO KVO 是 key value observing 的简写:苹果官方文档的解释:Key-value o...

  • KVO-KVC的原理探究 - KVO篇

    关于KVO的探究 KVO的基本使用 创建Person类,添加属性age: 在ViewController中添加属性...

  • iOS开发·KVO用法,原理与底层实现: runtime模拟实现

    摘要:这篇文章首先介绍KVO的基本用法,接着探究 KVO (Key-Value Observing) 实现机制,并...

  • 自定义KVO

    导语: 如果对KVO原理不是很熟悉的,可以参考下简书另一篇文章《ios KVO原理探究》,主要是通过模拟KVO底层...

  • KVO进阶——KVO实现探究

    本篇会对KVO的实现进行探究,不涉及太多KVO的使用方法,但是会有一些使用时的思考。 一、使用上的疑问 1.key...

  • iOS开发·KVO用法,原理与底层实现: runtime模拟实现

    本文Demo传送门:CMKVODemo 摘要:这篇文章首先介绍KVO的基本用法,接着探究 KVO (Key-Val...

网友评论

      本文标题:KVO探究(一)

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