------------------KVC---------------------
KVC是一种通过字符串来访问实例对象属性或变量的机制,使用最多的是用来字典转模型。利用runtime获取对象的所有成员变量, 再根据kvc键值赋值,进行字典转模型
当给对象发送setValue:forKey 消息时要判断对象是否存在key所对应的属性,如果有,直接赋值;如果没有就调用undefinedKey(默认崩溃,需要重写)
setValue:forKey的调用顺序
- 首先会按照setkey,_setKey顺序找方法
- 如果没有那么按 _key, _isKey,key, iskey的顺序搜索成员变量名
- 如何还没找到就调用setValue:forUndefinedKey:(默认崩溃,需要重写)
ValueForKey的调用顺序
- 首先会按照getKey, key, isKey,_key顺序找方法
- 如果没有那么按 _key, _isKey,key, iskey的顺序搜索成员变量名
- 如何还没找到就调用valueForUndefinedKey
------------------KVO---------------------
KVO是观察者模式的实现,使用了ISA混写技术
当被监听对象某个属性发生改变时,监听该属性值变化的对象可以接受到通知,然后通过kvo提供的系统的方法响应一些操作,有利于两个类间的解耦
原理
KVO是基于runtime机制实现的,某个对象被观察时,runtime会在运行时动态创建一个新的继承被监听类的子类(NSKVONotifying_ 开头)
1、然后将被监听类的对象的isa指针指向子类类对象;
2、并重写子类被观察属性的setter 方法,(这个重写是在运行时而不是编译时实现的,而且是KVO本质);当被监听对象的属性改变时,会触发set方法,但这个方法被重写了,并且在内部调用了didChangeValueForKey方法从而触发通知机制
KVO机制的特点和触发前提
- 修改的是属性
- 通过setter方法直接修改
- 通过 KVC (内部会调用setter方法,从而触发KVO)
-
修改的是成员变量
- KVC修改内部会触发KVO
- 手动触发
KVO使用步骤
- 注册观察者,实施监听
[self.p1 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
- 在回调方法中处理属性发生的变化
// 这个方法时属于 NSObject 类的,任何对象都可以作为观察者
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
NSLog(@"监听到了%@的%@属性发生了改变", object, keyPath);
NSLog(@"%@", change);
}
- 移除观察者
[p1 removeObserver:self forKeyPath:@"name"];
- 子类重写被观察属性的setter方法的内部实现
- (void)setName:(NSString *)name
{
[self willChangeValueForKey:@"age"];
[super setName:name];
[self didChangeValueForKey:@"age"];
// 这两个方法底层会调用observer的- (void)observeValueForKeyPath: ofObject: change: context:这个方法
}
拓展
- KVO和notification(通知)的区别?
- 两者都是一对多
- notification的优点是监听不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广
- KVO与delegate的不同?
KVO和NSNotification 都是负责发送接收通知,剩下的事情由系统处理,所以不用返回值; delegate 则需要代理联系
delegate一般是一对一,而这两个可以一对多
另外需要注意的是,由于KVO这种继承方式的注入是在运行时而不是编译时实现的,如果给定的实例没有观察者,那么KVO不会有任何开销,因为此时根本就没有KVO代码存在。但是即使没有观察者,委托和NSNotification还是得工作,这也是KVO此处零开销观察的优势
总结
对比其他的回调方式,KVO机制的运用的实现,更多的由系统支持,相比notification、delegate等更简洁些,并且能够提供观察属性的最新值以及原始值;但是相应的在创建子类、重写方法等等方面的内存消耗是很巨大的
1、异步:监听通知 主线程:发出通知 接收通知代码在主线程
2、主线程:监听通知 异步:发出通知 接收通知代码在异步
总结:接收通知代码 由 发出通知线程决定, KVO也一样