翾燚 阅读(7) 评论(0)

概述

KVOKey-Value Observing,它允许一个对象被另一个对象在改变指定的属性值后进行通知。iOS中的应用场景很多,比如model的值发生变化,controller里对model进行监听从而改变view。便于对KVO有更好地理解,可以先理解KVC:苹果官方KVC文档。最直接有效的学习方法是官方文档:KVO官方文档,这个文档还有日文版,很是诧异!!!

 

原理

系统为被观察者生成一个子类,命名方式NSKVONotyfing_类名,当监听的键值发生改变时,系统调用重写的子类set方法对该键进行赋值,并在内部加一个消息传递,让观察者去接受值的变化并可以进行相应地操作。

 

实际运用

KVO的实现采用了观察者模式,使用大致流程是先注册观察者被观察者等,然后改变要观察的键值,最后在观察者的消息接收方法中根据改变后的键值进行相应的处理。

iOS在NSObject、NSArray、NSOrderSet、NSSet中都加入了实现KVO的分类,而我们平时使用的大部分对象都是这些类的子类,所以系统KVO能够满足大部分需求。

  • 使用KVO要先进行注册,确定观察者、被观察者及要监听的值对象。使用分类NSKeyValueObserverRegistration的方法进行注册。

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

  • observer参数是接收消息的对象
  • keyPath是要观察的键名
  • options是一个枚举,包含四个枚举值:

NSKeyValueObservingOptionNew:接收消息方法的change字典中有新改变的值。

NSKeyValueObservingOptionOld:接收消息方法的change字典中有改变前一次的值。

NSKeyValueObservingOptionInitial:立即向接收消息方法发送通知,简言之就是你的注册方法可能还没执行完,已经接收到消息了。

NSKeyValueObservingOptionPrior:键值改变前后都会发送消息。

  • context是用来区分相同被观察者的不同键值或者不同被观察者同一或不同键值的标识,防止混淆。

 

知晓了注册方法,我们先声明一个Person类,添加两个属性name和age。在ViewController里的类扩展中添加一个Person类型的属性person,viewDidLoad方法里添加注册方法。

 

- (void)viewDidLoad {

    [super viewDidLoad];

    self.person = [[Person alloc] init];

    [self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNewcontext:nil];

    NSLog(@"观察者注册完毕");

}

  •  接下来改变监听的键值,触发系统动态添加的子类的setName方法,改变键值后通知观察者接收消息。添加一个触摸屏幕事件,在里面改变name的值

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    self.person.name = @"小明";

}

  •   现在使用观察者的接收消息方法,处理键值变化后的情况。

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;

  • keyPath是观察的键的名称。
  • object是被观察对象。
  • change是一个变化前后值的字典,包括改变前后的值等,和注册方法的options有关。
  • context是在注册时用来区分判定键的标识。

 

我们在该方法中实现一个NSLog方法,打印上述内容信息。NSKeyValueChangeKey是一个封装的const,包括NSKeyValueChangeKindKeyNSKeyValueChangeNewKeyNSKeyValueChangeOldKeyNSKeyValueChangeIndexesKeyNSKeyValueChangeNotificationIsPriorKey

 

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{

    NSLog(@"监听到%@对象的%@属性的改变为%@",[object class],keyPath,[change objectForKey:NSKeyValueChangeNewKey]);

}

  • 打印结果:

从结果看出来,消息接收方法收到Person的name变化。

以上就是KVO的简单实现过程。