KVO与KVC的实现原理

一、KVO的实现原理

KVO的全称是Key-ValueObserving(键值监听),可以用于监听某个对象属性值的改变。

1、KVO的使用API如下


通过addObserver: forKeyPath: options: context:添加观察者对某个属性的监听。

2、大家看下我写的代码,在设置age打个断点来调试。


为什么会这样?明明我的person实例是HPPerson类实例化而来的切发现他的类对象不再是HPPerson了,变成了NSKVONotifying_HPPerson。person1的类对象还是HPPerson。

注意:不能用[self.person class]来获取解析本质。[self.person class],[self.person1 class]得到的结果都是HPPerson类,这个可能是苹果API不想你知道太多内层的实现哈哈。所以要利用isa指针的本质来看问题。

说明:在使用[self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];时,在内部实现中通过Runtime动态生成了一个NSKVONotifying_HPPerson类。

3、NSKVONotifying_HPPerson类是什么类?

先看下setAge的实现。


可以看出,由于person加了KVO监听,所以setAge的函数地址变了,实现也变了!setAge里面的具体实现是调用了_NSSetIntValueAndNotify()这个函数。但是我们从self.person.age=10;的设置结果可以看出,self.person.age设值后的age确实是10;也就是说这个成员变量_age是正确按照setAge:里面的实现(_age=age)做了赋值操作。而这个赋值操作时在Person类对象的方法列表中的setAge方法里面。说明_NSSetIntValueAndNotify()函数应该实现了setAge的方法实现。也就是要达到这种目的,只有一种情况,NSKVONotifying_HPPerson是HPPerson的子类。也就是在addObserver:时候Runtime动态创建的NSKVONotifying_HPPerson类是HPPerson类的子类。

可以通过LLDB打印他的superclass指针来证明NSKVONotifying_HPPerson确实是HPPerson子类。


由此可以看出添加KVO后的实例对象的类对象变成了NSKVONotifying_HPPerson类,而没有添加KVO的实例对象的类对象还是之前的HPPerson类。

总结关系如下图:

1)添加KVO的类关系图谱


2)没有添加KVO的类关系图谱


3)如果增加一个stirng类型的name属性,则会调用_NSSetCharValueAndNotify(),由此可以知道_NSSet**ValueAndNotify()有非常多个这个函数,根据不同类型属性调用不同函数名的函数,这个属于Foundation框架下的函数,可以通过越狱设备拿到Foundation的二进制文件,然后反编译后去查找这类函数的实现。过程太复杂,这里不深入写下去了。 在这里只要知道我们的KVO实质是利用了Runtime生成了一个子类,在子类的setAge方法中调用_NSSetIntValueAndNotify()来实现的。

4)_NSSetIntValueAndNotify()这系列函数内部实现逻辑大概是如下这样:

通过willChangeValueForKey与didChangeValueForKey的调用,可以实现手动触发KVO监听。

所以可以大概知道这个函数内部实现应该是如下:


在didChangeValueForKey:里面实现了内部调用属性观察者observer的observeValueForKeyPath:ofObject:change:context:方法。

4、通过KVC设值age会不会触发KVO监听吗?


结果可以发现,KVC也会触发KVO监听。其内部也是实现了willChangeValueForKey与didChangeValueForKey的调用。

二、KVC设值取值原理流程图

常用API使用:

- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;

- (void)setValue:(id)value forKey:(NSString *)key;

- (id)valueForKeyPath:(NSString *)keyPath;

- (id)valueForKey:(NSString *)key;

1、设值原理

说明:+(BOOL)accessInstanceVariablesDirectly方法默认返回值YES;

2)取值原理


对于KVC的设值取值的顺序,可以自己建一个类把对应方法写进去看下执行及顺序。

三、总结

0、利用addObserver:forKeyPath...函数增加观察值变化,内部实现会利用Runtime动态生成一个NSKVONotifying_HPPerson类(HPPerson的子类)。NSKVONotifying_HPPerson类中也有setAge方法,实现代码是直接调用了Foundation下的_NSSet***ValueAndNotify()系里函数。

1、_NSSet***ValueAndNotify()系里函数实现逻辑为调用willChangeValueForKey与didChangeValueForKey方法来实现。

2、-(void)didChangeValueForKey方法内部实现[observer observeValueForKeyPath: ofObject...]的调用。

3、KVO需要通过set方法来实现键值监听,如果直接赋值成员变量是不会触发KVO。

4、KVO可以通过手动调用-(void)willChangeValueForKey与-(void)didChangeValueForKey方法来实现。

5、KVC设值会触发KVO。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容