KVC与KVO

一、KVO

KVO全称Key-Value-Observing,作用是对象监听另一个对象特定的属性的改变,并在改变时接收到事件,举例如下:

如果person对象的name属性改变了,就会调用监听者BaseController的observeValueForKeyPath方法。

1.1 KVO基本使用

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

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

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

1.2 KVO实现原理

1)为原生的类生成一个名为NSKVONotifying_***的子类,子类重写了属性的set方法,里面调用了willChangeValueForKey和didChangeValueForKey,还重写了class方法,让其看着更像原类,然后obj的isa指向了监听类。set方法大致就如下图所示:

2)在调用obj的属性set方法时,在didChangeValueForKey:方法中自动调用了所有Observe的observeValueForKeyPath:ofObject:change:context: 方法。这样就实现了属性监听。

1.2.1 KVO中间类验证:

在添加监听的addObserver:forKeyPath:options:context:方法下面得到obj的Class类和父类,代码如下:

打印效果如下:

1.2.2 验证 didChangeValueForKey: 内部调用监听方法

在Person类中,重写重写 willChangeValueForKey: 和 didChangeValueForKey: 方法,模拟它们的实现。具体代码如下:

打印效果如下:

二、bk中的KVO

BlockKit第三方库中有个NSObject_BKBlockObservation.m的文件,提供了很方便加入Observe功能的一些API,比如- (NSString *)bk_addObserverForKeyPath:(NSString *)keyPath task:(void (^)(id target))task方法,主要解决传统KVO的以下几个问题:

1)KVO使用不当很容易导致崩溃,比如重复add和remove、Observer被释放、keyPath传错等。

2)写起来太麻烦,每个Observe都得重写observeValueForKeyPath:ofObject:change:context:

2.1 bk的KVO的原理

在添加Observer时,最终都会调入接口

- (void)bk_addObserverForKeyPaths:(NSArray *)keyPaths

identifier:(NSString *)identifier

options:(NSKeyValueObservingOptions)options

context:(BKObserverContext)context

task:(id)task

传参说明如下:

参数名称(类型)

备注

keypaths(NSArray *)

所有被监听的属性

identifier(NSString *)

这次添加监听的唯一ID

options(NSKeyValueObservingOptions)

监听task中需要回传的参数

context(BKObserverContext)

task需要回传什么

typedef NS_ENUM(int, BKObserverContext) {
//只回传被监听的对象
	BKObserverContextKey,
//Change+对象	BKObserverContextKeyWithChange,
//keyPath+对象
	BKObserverContextManyKeys,
//Change+keyPath+对象	BKObserverContextManyKeysWithChange
};

task(id)

具体的block

具体代码如下:

2.2 _BKObserver对象

_BKObserver其实就是一个监听对象,是task和被监听对象的中介者。实现了

- (void)observeValueForKeyPath:(NSString *)keyPath

ofObject:(id)object

change:(NSDictionary *)change

context:(void *)context

来具体调用task。

2.2.1 _BKObserver的构造方法:

2.2.2 _BKObserver开始监听startObservingWithOptions

startObservingWithOptions是开启监听的入口,对被监听对象添加所有keyPath的监听,具体代码如下:

2.2.3 _BKObserver中的监听方法

_BKObserver类的监听方法,当keyPath发生变化时,调用task回调,具体代码如下:

2.3 dealloc中的删除所有监听

入口函数是bk_removeAllBlockObservers,具体代码如下:

stopObserving函数又调用了_stopObservingLocked,作用就是将被监听对象添加的所有keyPath的监听全部remove。具体代码如下:

三、KVC

KVC全称是Key Value Coding,KVC是一种利用字符串来访问对象的属性方法或者成员变量机制。原理主要依赖与setter和getter方法。另外accessInstanceVariablesDirectly表示在搜索的过程中是否允许读取实例变量的值,默认为YES。

3.1 赋值原理

以setValue:forKey:为例,运行步骤如下:

1)以set<Key>、_set<Key>的顺序查找对应命名的setter方法,如果找到的话,调用这个方法并将值传进去。

2)如果没有找到setter方法,但是accessInstanceVariablesDirectly类方法返回YES,则按_<Key>、_is<Key>、<Key>、is<Key>的顺序查找一个对应的实例变量,如果找到那么将value赋值。

3)如果没有找到setter方法或实例变量,则调用setValue:forUndefinedKey:方法,默认是抛出异常,也可进行重写。

流程图如下:

3.2 取值原理

以valueForKey:为例,运行步骤如下:

1)以get<Key>、<Key>、is<Key>、_<Key>的顺序搜索方法,如果有,直接调用。

2)如果没有getter方法,但是accessInstanceVariablesDirectly类方法返回YES,那么按照_<Key>、_is<Key>、<Key>、is<Key>的顺序查找实例变量。

3)如果返回值是一个对象指针,则直接返回。如果是一个基础数据类型,而且Number类型支持,那么被Number封装再返回,如果不被支持,那么NSValue封装并返回。

4)在上述情况都失败的情况下调用valueForUndefinedKey:方法,默认是抛出异常,也可进行重写。

流程图如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值