首页 > IT职业 > IT职场 > MG--探究KVO的底层实现原理
/**
* 1. self.person要监听的对象
* 2. 参数说明
* @param addObserver 观察者负责处理监听事件的对象
* @param forKeyPath 要监听的属性
* @param options 观察的选项观察新、旧值也可以都观察
* @param context 上下文用于传递数据可以利用上下文区分不同的监听
*/
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person Name"];
/**
* 当监控的某个属性的值改变了就会调用
*
* @param keyPath 监听的属性名
* @param object 属性所属的对象
* @param change 属性的修改情况属性原来的值`oldValue`、属性最新的值`newValue`
* @param context 传递的上下文数据与监听的时候传递的一致可以利用上下文区分不同的监听
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"%@对象的%@属性改变了%@", object, keyPath, change);
}
KVOcolor{#dd0000}{KVO}KVO 是 Objective-C 对观察者模式Observer Pattern的实现。也是 Cocoa Binding 的基础。当被观察对象的某个属性发生更改时观察者对象会获得通知。
有意思的是你不需要给被观察的对象添加任何额外代码就能使用 KVOcolor{#dd0000}{KVO}KVO 。这是怎么做到的
第一次被观察时系统就会在运行期动态地创建该类的一个派生类在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制
@property (nonatomic, strong) NSDate *now;
- (void)viewDidLoad
{
[super viewDidLoad];
// “手动触发self.now的KVO”必写。
[self willChangeValueForKey:@"now"];
// “手动触发self.now的KVO”必写。
[self didChangeValueForKey:@"now"];
}

KVOcolor{#dd0000}{KVO}KVO的基础是KVCcolor{#0000FF}{KVC}KVCKVCcolor{#0000FF}color{#0000FF}{KVC}KVC运用了一个isa-swizzling技术. isa-swizzling就是类型混合指针机制, 将2个对象的isa指针互相调换, 就是俗称的黑魔法.
KVCcolor{#0000FF}{KVC}KVC主要通过isa-swizzling, 来实现其内部查找定位的. 默认的实现方法由NSOject提供isa指针, 如其名称所指,(就是is a kind of的意思), 指向分发表对象的类. 该分发表实际上包含了指向实现类中的方法的指针, 和其它数据。
- 具体主要分为三大步
第一步寻找该属性有没有setsetter方法有就直接赋值
第二步寻找有没有该属性带下划线的成员属性有就直接赋值
第三步寻找有没有该属性的成员属性有就直接赋值
- 或者这么说
1、首先搜索setKey:方法.(key指成员变量名, 首字母大写)
2、上面的setter方法没找到, 如果类方法accessInstanceVariablesDirectly返回YES. 那么按 _key, _isKeykey, iskey的顺序搜索成员名.(NSKeyValueCodingCatogery中实现的类方法, 默认实现为返回YES)
3、如果没有找到成员变量, 调用setValue:forUnderfinedKey:
比如说如下的一行KVC的代码
[object setValue:@"13123" forKey:@"uuid"];
就会被编译器处理成:
// 首先找到对应sel
SEL sel = sel_get_ uuid("setValue:forKey:");
// 根据object->isa找到sel对应的IMP实现指针
IMP method = objc_msg_lookup (object->isa,sel);
// 调用指针完成KVC赋值
method(object, sel, @"13123", @"uuid");
通过修改类的成员变量不会触发KVOcolor{#dd0000}{KVO}KVO,那为什么通过KVCcolor{#0000FF}{KVC}KVC的setValue:forkey: 给成员变量赋值会触发KVO呢?
首先KVOcolor{#dd0000}{KVO}KVO的基础是KVCcolor{#0000FF}{KVC}KVC我们看下KVCcolor{#0000FF}{KVC}KVC的底层实现
1、首先搜索setKey:方法.(key指成员变量名, 首字母大写)
2、上面的setter方法没找到, 如果类方法accessInstanceVariablesDirectlycolor{#00DD00}{accessInstanceVariablesDirectly}accessInstanceVariablesDirectly返回YES. 那么按 _key, _isKeykey, iskey的顺序搜索成员名。
如果找到了就会触发KVO因为底层内部会调用willChangeValueForKey:color{#00DD00}{willChangeValueForKey:}willChangeValueForKey: 和 didChangeValueForKey:color{#00DD00}{didChangeValueForKey:}didChangeValueForKey:方法。
你可以重写该类的调用willChangeValueForKey:color{#00DD00}{willChangeValueForKey:}willChangeValueForKey: 和didChangeValueForKey:color{#00DD00}{didChangeValueForKey:}didChangeValueForKey:方法去验证当KVC改变属性值的时候比如Person继承NSObject它有一个成员变量@public int _age;[self.person1 setValue:@(10) forKey:@“age”]; 会调用willChangeValueForKey:color{#00DD00}{willChangeValueForKey:}willChangeValueForKey: 和 didChangeValueForKey:color{#00DD00}{didChangeValueForKey:}didChangeValueForKey:方法。所以会触发KVO。
通过修改类的成员变量不会触发KVO因为成员变量不会生成setter方法直接访问成员变量自然不会触发KVO而要触发KVO本质是必须调用调用willChangeValueForKey:color{#00DD00}{willChangeValueForKey:}willChangeValueForKey: 和 didChangeValueForKey:color{#00DD00}{didChangeValueForKey:}didChangeValueForKey:方法。KVO的底层实现也是通过重写setter方法 setter方法里面调用willChangeValueForKey:color{#00DD00}{willChangeValueForKey:}willChangeValueForKey:和 didChangeValueForKey:color{#00DD00}{didChangeValueForKey:}didChangeValueForKey:方法。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/4550/viewspace-2822616/ ,如需转载,请注明出处,否则将追究法律责任。