美文网首页
(IOS)KVO

(IOS)KVO

作者: rightmost | 来源:发表于2018-12-29 17:02 被阅读0次

    概述

    KVO全称为Key Value Observing,键值监听机制,由NSKeyValueObserving协议提供支持,NSObject类继承了该协议,所以NSObject的子类都可使用该方法。

    KVO全称KeyValueObserving,是苹果提供的一套事件通知机制。允许对象监听另一个对象特定属性的改变,并在改变时接收到事件。由于KVO的实现机制,所以对属性才会发生作用,一般继承自NSObject的对象都默认支持KVO。

    KVO和NSNotificationCenter都是iOS中观察者模式的一种实现。区别在于,相对于被观察者和观察者之间的关系,KVO是一对一的,而一对多的。KVO对被监听对象无侵入性,不需要修改其内部代码即可实现监听。

    KVO可以监听单个属性的变化,也可以监听集合对象的变化。通过KVC的mutableArrayValueForKey:等方法获得代理对象,当代理对象的内部对象发生改变时,会回调KVO监听的方法。集合对象包含NSArray和NSSet。

    基础使用

    使用KVO分为三个步骤:

    通过addObserver:forKeyPath:options:context:方法注册观察者,观察者可以接收keyPath属性的变化事件。

    在观察者中实现observeValueForKeyPath:ofObject:change:context:方法,当keyPath属性发生改变后,KVO会回调这个方法来通知观察者。

    当观察者不需要监听时,可以调用removeObserver:forKeyPath:方法将KVO移除。需要注意的是,调用removeObserver需要在观察者消失之前,否则会导致Crash。

    注册方法

    在注册观察者时,可以传入options参数,参数是一个枚举类型。如果传入NSKeyValueObservingOptionNew和NSKeyValueObservingOptionOld表示接收新值和旧值,默认为只接收新值。如果想在注册观察者后,立即接收一次回调,则可以加入NSKeyValueObservingOptionInitial枚举。

    还可以通过方法context传入任意类型的对象,在接收消息回调的代码中可以接收到这个对象,是KVO中的一种传值方式。

    在调用addObserver方法后,KVO并不会对观察者进行强引用,所以需要注意观察者的生命周期,否则会导致观察者被释放带来的Crash。

    监听方法

    观察者需要实现observeValueForKeyPath:ofObject:change:context:方法,当KVO事件到来时会调用这个方法,如果没有实现会导致Crash。change字典中存放KVO属性相关的值,根据options时传入的枚举来返回。枚举会对应相应key来从字典中取出值,例如有NSKeyValueChangeOldKey字段,存储改变之前的旧值。

    change中还有NSKeyValueChangeKindKey字段,和NSKeyValueChangeOldKey是平级的关系,来提供本次更改的信息,对应NSKeyValueChange枚举类型的value。例如被观察属性发生改变时,字段为NSKeyValueChangeSetting。

    如果被观察对象是集合对象,在NSKeyValueChangeKindKey字段中会包含NSKeyValueChangeInsertion、NSKeyValueChangeRemoval、NSKeyValueChangeReplacement的信息,表示集合对象的操作方式。

    KVO实现步骤

    1.注册观察者(为被观察这指定观察者以及被观察者属性)

    /*

    options: 有4个值,分别是:

    NSKeyValueObservingOptionOld 把更改之前的值提供给处理方法

    NSKeyValueObservingOptionNew 把更改之后的值提供给处理方法

    NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。

    NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和值改变之后。

    */

    //注册一个监听器用于监听指定的key路径[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

    2.实现回调方法

    //当key路径对应的属性值发生改变时,监听器就会回调自身的监听方法,如下- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)contex

    }

    3.触发回调方法

    注意:路径keyPath请参考上一篇KVO中的说明

    4.移除观察者

    //删除指定的key路径监听器

    [self.person removeObserver:self forKeyPath:@"name"];

    //删除指定的key路径监听器,只是多了context参数

    [self.person removeObserver:self forKeyPath:@"name" context:nil];

    KVO的主要应用场景

    应用场景:当数据模型的数据发生改变时,视图组件能动态的更新,及时显示数据模型更新后的数据。

    比如:监听scrollView的contentOffset属性,来完成用户滚动时动态改变某些控件的属性实现效果,包括渐变导航栏、下拉刷新控件等效果。

    KVO的实现原理简要说明

    KVO 是基于 runtime 机制实现的 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一 个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。派生类在 被重写的 setter 方法内实现真正的通知机制。

    如果原类为 Person,那么生成的派生类名为 NSKVONotifying_Person 每个类对象中都有一个 isa 指针指向当前类,当一个类对象的第一次被观察,那么 系统会偷偷将 isa 指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的 setter 方法。

    键值观察通知依赖于 NSObject 的两个方法willChangeValueForKey:,didChangevlueForKey:

    在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey: 会被调用,

    继而observeValueForKey:ofObject:change:context: 也会被调用。

    补充:KVO 的这套实现机制中苹果还偷偷重写了 class 方法,让我们误认为还是使用的当前类,从而达到隐藏生成的派生类。

    注意点

    KVO的addObserver和removeObserver需要是成对的,如果重复remove则会导致NSRangeException类型的Crash,如果忘记remove则会在观察者释放后再次接收到KVO回调时Crash。

    相关文章

      网友评论

          本文标题:(IOS)KVO

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