老掉牙的题目了,只是为了总结下知识点,还有新进门的iOS。不喜勿喷!
KVO 前言:
我们知道iOS中NSObject对象可以增加观察者来监听对象属相值得变化。
1.那么KVO的本质是什么呢?
2. KVO可以手动触发么?
下面我们就来看下KVO的实现
从上图我们可以看出,当实现KVO之后,p1的isa指针指向的发生了改变,指向了一个NSKVONotifying_JWPerson,那么这个类是从哪来的呢?
其实呢,KVO的本质就是:当使用KVO之后,iOS系统会修改这个对象的isa指针,改为指向一个全新通过Runtime动态创建的子类,子类拥有自己的set方法实现,内部会调用willChangeValueForKey 、 原来的set方法 、 didChangeValueForKey(这个方法内部会调用监听器的监听方法)来实现监听的目的的。
下面我们就用来代码来大概还原下KVO的过程:
这就是KVO的本质工作原理。其实并不是很难,有很容易理解。下面我们在来证实下我们所说的对不对。
从上图中我们可以看出,其实实现监听之后,setAge方法并不是走向原来的父类的setAge方法,而是走了_NSSETIntVAalueNotify这个方法。。这个方法是一个底层的C语言方法,里面调用了
willChangeValueForKey:
[super setAge:]
didChangeValueForKey: 在此处调用[observerobserveValueForKeyPath:@"age" ofObject:(object change:change context:context];l来进行回调,达到监听值发生改变的原理。
还有一个就是:Runtime动态生成的子类,只是重写了setter方法么?答案显然并不是,那他都有哪些方法呢?
从图中我们可以看出NSKVONotifying_JWPerson 一共有四个方法,一个是set方法,一个是class、delloc、_isKVOA ,下面我们来说下这四个方法:
1.setter方法不多说了,上面已经介绍了。
2.class方法的作用是什么呢?答案就是:当我们调用[object class]方法的时候,如果子类没有改方法,就会从父类中寻找,直至找到NSObject中的class方法,进行调用。而NSObject的class实现是返回 objc_getclass(self) 实现的,也就是说会直接返回自身类,到那时apple并不想让NSKVONotifying_JWPerson给暴露出来,所以重写class方法,进而返回你添加观察者的类。
3.delloc方法,是释放一些资源。
4.isKVOA 方法,我也不是很懂,有知道的同学,请留言。。我猜想应该是标识是否是KVO的一种状态使用的。
如何手动执行KVO呢?我们知道当值发生改变会触发观察者的方法调用,但是如何在我们不改变值得情况下触发改方法呢?
-(void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context ;
答案就是 需要手动触发的话 你需要将
[obj willChangeValueForKey:@"age"];
[obj didChangeValueForKey:@"age"];
这两行代码放置执行处,就会触发观察者的方法。这也说明了,触发KVO回调的就是通过这两个方法来实现的。
补充:
1.修改实例的成员变量会触发KVO么?
不会的,KVO是通过重写setter方法来进行监听值得改变,成员变量赋值是不会触发setter方法,因此也并不会触发KVO。
KVC 前言:
键值编码,通过setValue赋值的操作修改对象属性的值。那么KVC的原理又是什么样子的呢?下面我们就来揭晓一下。
赋值过程: 当操作setValue 的时候会寻找 setKey 和 _setKey 依次寻找两个方法的实现,如果实现,则赋值成功,如果没有首先会判断类方法accessInstanceVariablesDirectly 返回值(默认返回YES),返回NO,直接抛出异常,返回YES则会依次访问该类的成员变量里面的 _key 、__isKey、Key、isKey 是否存在,如果四个成员变量都不存在,爆出异常,存在赋值成功。
取值过程:当操作valueForKey 的时候会寻找 getKey 和 key、isKey、_key 依次寻找四个方法的实现,如果实现,则取值成功,如果没有首先会判断类方法accessInstanceVariablesDirectly 返回值(默认返回YES),返回NO,直接抛出异常,返回YES则会依次访问该类的成员变量里面的 _key 、__isKey、Key、isKey 是否存在,如果四个成员变量都不存在,抛出异常,存在取值成功。
小知识点:
1、[obj setValue:value forKey:@"key"]; 跟 [obj setValue:value forKeyPath:@"key"];的区别在于:
两者的区别在于,forKeyPath更加强大,可以访问对象内的对象的属性,比如super.son.property,而forKey只能访问当前类的属性。
2、KVC改变的赋值会出发KVO么?
会触发KVO的,这里触发分两种情况:
当key存在setter方法的时候,会进行KVO的监听,重写setter方法,进行触发KVO。
当key不存在setter方法的时候,只有成员变量存在的时候,也会触发KVO监听,是因为KVC内部调用了KVO得 willchangeForKey 和didChangeForKey方法来触发KVO。