KVO实现原理

来源:互联网 发布:质检总局网络培训学院 编辑:程序博客网 时间:2024/05/21 17:56

http://www.bjbkws.com/apply/1210/


什么是KVO ?

KVO即Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。
简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。


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. // 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. // breakpoint 3 

  15. (lldb) po [object_getClass(sark) instanceMethods] 

  16. <__NSArrayI 0x8e9cff0>( 

  17. - (void)setName:(id)arg0 , 

  18. - (void).cxx_destruct, 

  19. - (id)name 



大概就是说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.     NSLog(@"%@", NSStringFromSelector(_cmd)); 

  5.     [super willChangeValueForKey:key]; 

  6.  

  7. - (void)didChangeValueForKey:(NSString *)key 

  8.     NSLog(@"%@", NSStringFromSelector(_cmd)); 

  9.     [super didChangeValueForKey:key]; 

  10.  

  11. @end 



0 0
原创粉丝点击