zoukankan      html  css  js  c++  java
  • iOS 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 
    首先就有个扎眼的- .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.     NSLog(@"%@", NSStringFromSelector(_cmd)); 
    5.     [super willChangeValueForKey:key]; 
    6.  
    7. - (void)didChangeValueForKey:(NSString *)key 
    8.     NSLog(@"%@", NSStringFromSelector(_cmd)); 
    9.     [super didChangeValueForKey:key]; 
    10.  
    11. @end 
     
  • 相关阅读:
    4天精通arcgis
    性能优化紧急回顾笔记
    linux下oracle导入dmp文件
    centos虚拟机复制后网络重启出错解决
    redhat ent 6.5 virtualbox虚拟机通过桥接方式配置主机-虚拟机的局域网
    SVN的搭建及使用(三)用TortoiseSVN修改文件,添加文件,删除文件,以及如何解决冲突,重新设置用户名和密码等
    SVN 的搭建及使用(二)VisualSVN Server建立版本库,以及VisualSVN和TortoiseSVN的使用
    SVN 的搭建及使用(一)下载和搭建SVN服务器
    Visual Studio 2008常见问题
    .net 学习路线感想(转)
  • 原文地址:https://www.cnblogs.com/wfwenchao/p/4569287.html
Copyright © 2011-2022 走看看