码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-08-16 23:01:05      阅读:198      评论:0      收藏:0      [点我收藏+]

标签:

 

 

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. @implementation Sark
  5. @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. (lldb) po sark.class
  2. Sark
  3. (lldb) po object_getClass(sark)
  4. Sark

  5. // breakpoint 2
  6. (lldb) po sark.class
  7. Sark
  8. (lldb) po object_getClass(sark)
  9. NSKVONotifying_Sark

  10. // breakpoint 3
  11. (lldb) po sark.class
  12. Sark
  13. (lldb) po object_getClass(sark)
  14. 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. + (NSArray *)protocols;
  8. + (NSDictionary *)descriptionForProtocol:(Protocol *)proto;

  9. + (NSString *)parentClassHierarchy;
  10. @end

 

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

 


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

 

首先就有个扎眼的- .cxx_destruct冒出来,这货是个啥?详细的探究请参考我的另一篇文章 : http://blog.sunnyxx.com/2014/04/02/objc_dig_arc_dealloc/

 

大概就是说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. - (void)willChangeValueForKey:(NSString *)key
    3. {
    4. NSLog(@"%@", NSStringFromSelector(_cmd));
    5. [super willChangeValueForKey:key];
    6. }

    7. - (void)didChangeValueForKey:(NSString *)key
    8. {
    9. NSLog(@"%@", NSStringFromSelector(_cmd));
    10. [super didChangeValueForKey:key];
    11. }

    12. @end 

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

标签:

原文地址:http://www.cnblogs.com/xiaochuan4938/p/4735121.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!