Objective-C KVO简单探索

移动开发 iOS
KVO(Key Value Observing),是观察者模式在Foundation中的实现。当一个object有观察者时,动态创建这个object的类的子类,对于每个被观察的property,重写其set方法;当一个property没有观察者时,删除重写的方法,当没有observer观察任何一个property时,删除动态创建的子类。

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

KVO的原理

简而言之就是:

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

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

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

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

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

空说无凭,简单验证下。

  1. @interface Sark : NSObject  
  2. @property (nonatomic, copy) NSString *name;  
  3. @end  
  4.   
  5. @implementation Sark  
  6. @end 
  1. Sark *sark = [Sark new];  
  2. // breakpoint 1  
  3. [sark addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];  
  4. // breakpoint 2  
  5. sark.name = @"萨萨萨";  
  6. [sark removeObserver:self forKeyPath:@"name"];  
  7. // breakpoint 3  

断住后分别使用- class和object_getClass()打出sark对象的Class和真实的Class

  1. // breakpoint 1  
  2. (lldb) po sark.class  
  3. Sark  
  4. (lldb) po object_getClass(sark)  
  5. Sark  
  6.   
  7. // breakpoint 2  
  8. (lldb) po sark.class  
  9. Sark  
  10. (lldb) po object_getClass(sark)  
  11. NSKVONotifying_Sark  
  12.   
  13. // breakpoint 3  
  14. (lldb) po sark.class  
  15. Sark  
  16. (lldb) po object_getClass(sark)  
  17. Sark  

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

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

  1. @interface NSObject (DLIntrospection)  
  2. + (NSArray *)classes;  
  3. + (NSArray *)properties;  
  4. + (NSArray *)instanceVariables;  
  5. + (NSArray *)classMethods;  
  6. + (NSArray *)instanceMethods;  
  7.   
  8. + (NSArray *)protocols;  
  9. + (NSDictionary *)descriptionForProtocol:(Protocol *)proto;  
  10.   
  11. + (NSString *)parentClassHierarchy;  
  12. @end  

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

  1. // breakpoint 1  
  2. (lldb) po [object_getClass(sark) instanceMethods]  
  3. <__NSArrayI 0x8e9aa00>(  
  4. - (void)setName:(id)arg0 ,  
  5. - (void).cxx_destruct,  
  6. - (id)name  
  7. )  
  8. // breakpoint 2  
  9. (lldb) po [object_getClass(sark) instanceMethods]  
  10. <__NSArrayI 0x8d55870>(  
  11. - (void)setName:(id)arg0 ,  
  12. - (class)class,  
  13. - (void)dealloc,  
  14. - (BOOL)_isKVOA  
  15. )  
  16. // breakpoint 3  
  17. (lldb) po [object_getClass(sark) instanceMethods]  
  18. <__NSArrayI 0x8e9cff0>(  
  19. - (void)setName:(id)arg0 ,  
  20. - (void).cxx_destruct,  
  21. - (id)name  
  22. )  

首先就有个扎眼的- .cxx_destruct冒出来,这货是个啥?详细的探究请参考我的另一篇文章

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

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

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

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

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

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

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

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

  1. @implementation Sark  
  2.   
  3. - (void)willChangeValueForKey:(NSString *)key  
  4. {  
  5.     NSLog(@"%@", NSStringFromSelector(_cmd));  
  6.     [super willChangeValueForKey:key];  
  7. }  
  8.   
  9. - (void)didChangeValueForKey:(NSString *)key  
  10. {  
  11.     NSLog(@"%@", NSStringFromSelector(_cmd));  
  12.     [super didChangeValueForKey:key];  
  13. }  
  14.   
  15. @end  

 

责任编辑:闫佳明 来源: blog.sunnyxx
相关推荐

2011-07-19 17:24:31

Objective-C 对象

2011-07-27 16:55:12

Objective-c 闭包

2011-08-10 18:07:29

Objective-C反射

2013-06-20 10:40:32

Objective-C实现截图

2011-05-11 11:20:26

Objective-C

2011-05-11 15:58:34

Objective-C

2013-03-27 12:54:00

iOS开发Objective-C

2013-08-21 13:26:43

Objective-CNSDate说明

2011-05-11 13:54:08

Objective-C

2011-05-11 15:45:50

内存管理Objective-C

2011-08-04 14:58:37

Objective-C Cocoa NSString

2011-08-02 13:16:36

Objective-C 语法 函数

2011-08-04 11:15:46

Objective-C 构造函数 构造方法

2011-05-11 14:06:49

Objective-C

2013-08-21 14:57:42

objective-c问题

2011-08-04 09:35:09

Objective-C 编码规范

2011-08-03 16:55:05

Objective-C 代理

2012-06-15 09:47:48

Objective-CCategory

2011-07-29 16:16:30

Objective-c block

2011-07-08 13:49:46

Objective-C UUID
点赞
收藏

51CTO技术栈公众号