如何理解 Objective-C编程的KVO 原理

本文来源于:cocoachina

KVO(Key Value Observing),是观察者模式在Foundation中的实现。

KVO的原理

简而言之就是:

1、当一个object有观察者时,动态创建这个object的类的子类

2、对于每个被观察的property,重写其set方法

3、在重写的set方法中调用- willChangeValueForKey:和- didChangeValueForKey:通知观察者

4、当一个property没有观察者时,删除重写的方法

5、当没有observer观察任何一个property时,删除动态创建的子类

空说无凭,简单验证下。

@interface Sark : NSObject @property (nonatomic, copy) NSString *name; @end @implementation Sark @end

Sark *sark = [Sark new]; // breakpoint 1 [sark addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil]; // breakpoint 2 sark.name = @"萨萨萨"; [sark removeObserver:self forKeyPath:@"name"]; // breakpoint 3 断住后分别使用- class和object_getClass()打出sark对象的Class和真实的Class

// breakpoint 1 (lldb) po sark.class Sark (lldb) po object_getClass(sark) Sark // breakpoint 2 (lldb) po sark.class Sark (lldb) po object_getClass(sark) NSKVONotifying_Sark // breakpoint 3 (lldb) po sark.class Sark (lldb) po object_getClass(sark) Sark 上面的结果说明,在sark对象被观察时,framework使用runtime动态创建了一个Sark类的子类NSKVONotifying_Sark,而且为了隐藏这个行为,NSKVONotifying_Sark重写了- class方法返回之前的类,就好像什么也没发生过一样。但是使用object_getClass()时就暴露了,因为这个方法返回的是这个对象的isa指针,这个指针指向的一定是个这个对象的类对象

然后来偷窥一下这个动态类实现的方法,这里请出一个NSObject的扩展NSObject+DLIntrospection,它封装了打印一个类的方法、属性、协议等常用调试方法,一目了然。

@interface NSObject (DLIntrospection) + (NSArray *)classes; + (NSArray *)properties; + (NSArray *)instanceVariables; + (NSArray *)classMethods; + (NSArray *)instanceMethods; + (NSArray *)protocols; + (NSDictionary *)descriptionForProtocol:(Protocol *)proto; + (NSString *)parentClassHierarchy; @end

然后继续在刚才的断点处调试:

// breakpoint 1 (lldb) po [object_getClass(sark) instanceMethods] ( - (void)setName:(id)arg0 , - (void).cxx_destruct, - (id)name ) // breakpoint 2 (lldb) po [object_getClass(sark) instanceMethods] ( - (void)setName:(id)arg0 , - (class)class, - (void)dealloc, - (BOOL)_isKVOA ) // breakpoint 3 (lldb) po [object_getClass(sark) instanceMethods] ( - (void)setName:(id)arg0 , - (void).cxx_destruct, - (id)name )

大概就是说arc下这个方法在所有dealloc调用完成后负责释放所有的变量,当然这个和KVO没啥关系了,回到正题。

从上面breakpoint2的打印可以看出,动态类重写了4个方法:

1、- setName:最主要的重写方法,set值时调用通知函数

2、- class隐藏自己必备啊,返回原来类的class

3、- dealloc做清理犯罪现场工作

4、- _isKVOA这就是内部使用的标示了,判断这个类有没被KVO动态生成子类

接下来验证一下KVO重写set方法后是否调用了- willChangeValueForKey:和- didChangeValueForKey:

最直接的验证方法就是在Sark类中重写这两个方法:

@implementation Sark - (void)willChangeValueForKey:(NSString *)key {NSLog(@"%@", NSStringFromSelector(_cmd));[super willChangeValueForKey:key]; } - (void)didChangeValueForKey:(NSString *)key {NSLog(@"%@", NSStringFromSelector(_cmd));[super didChangeValueForKey:key]; } @endobjective-c编程之道:ios设计模式解析

obj-c高级编程

[objective-c 程序设计(第4版)].(斯蒂芬).林冀等.扫描版

我只有这些

要的话留邮箱

cocoachina

又送分了,除了cocoaChina还有什么iOS的开发论坛?

我觉得cocoachina主要是把经历放在产品上,自己运营游戏,而eoeandroid主要是运营商店如果说移动领域的技术社区还得说devdiv,目前看来只有devdiv获得了厂家的广泛认可如果是老会员,你就能发现,无论是google、三星、微软、诺基亚都在devdiv投放了大量的广告,当然广告都是针对开发者的,这些国外大厂都在扶持devdiv如果一个技术社区没有厂商的大量支持是很难走社区化道路的,我目前没有看到有大量的厂商扶持cocoachina和eoeandroid,另外他们平台过于单一,如果cocoachina想要找厂商扶持只能找苹果,但是苹果不扶持国内的开发者eoeandroid只能找google或者android厂商,但是google比较牛叉,也不太拿国内开发者当回事android厂商其实是一个合作点,eoe本来有机会,但是似乎公关不行,其实eoe社区是真假,垃圾回复满天飞,根本看不出来像个技术社区,我想就是这个原因三星选择了和devdiv合作。其它的不说,DevDiv水区肯定是最NB的,因为我也是茫茫中一员恨死CSDN了,把我的账号曝光了~~~害的我改了N久的账号cocoaChina 以前还是很不错的~~被收之后,就没人做事了~個人感覺,cocoachina已經是商業化的論壇它是一個商業平台,專注在時事,廣告,行銷等不能算是技術平台現在反而我只是偶爾會去cocoachina看一下業界訊息帳戶可能一兩個月才登入那一兩次cocoa现在没人维护了,全站变水区了,还是来DEVDIV吧DEVDIV技术区没人灌水,水区有人问技术,风景独特我觉得cocoachina主要是把经历放在产品上,自己运营游戏,而eoeandroid主要是运营商店如果说移动领域的技术社区还得说devdiv,目前看来只有devdiv获得了厂家的广泛认可如果是老会员,你就能发现,无论是google、三星、微软、诺基亚都在devdiv投放了大量的广告,当然广告都是针对开发者的,这些国外大厂都在扶持devdiv如果一个技术社区没有厂商的大量支持是很难走社区化道路的,我目前没有看到有大量的厂商扶持cocoachina和eoeandroid,另外他们平台过于单一,如果cocoachina想要找厂商扶持只能找苹果,但是苹果不扶持国内的开发者eoeandroid只能找google或者android厂商,但是google比较牛叉,也不太拿国内开发者当回事android厂商其实是一个合作点,eoe本来有机会,但是似乎公关不行,其实eoe社区是真假,垃圾回复满天飞,根本看不出来像个技术社区,我想就是这个原因三星选择了和devdiv合作。