KVO实现原理
2016-06-02 12:56
609 查看
KVO(Key Value Observing),是
简而言之就是:
当一个object有观察者时,动态创建这个object的类的子类
对于每个被观察的property,重写其
在重写的
当一个property没有观察者时,删除重写的方法
当没有observer观察任何一个property时,删除动态创建的子类
空说无凭,简单验证下。
测试代码:
断住后分别使用
上面的结果说明,在sark对象被观察时,framework使用
而且为了隐藏这个行为,NSKVONotifying_Sark重写了
但是使用
然后来偷窥一下这个动态类实现的方法,这里请出一个NSObject的扩展
然后继续在刚才的断点处调试:
首先就有个扎眼的
大概就是说arc下这个方法在所有
从上面breakpoint2的打印可以看出,动态类重写了4个方法:
接下来验证一下KVO重写set方法后是否调用了
最直接的验证方法就是在Sark类中重写这两个方法:
没问题。
观察者模式在
Foundation中的实现
KVO的原理
简而言之就是:当一个object有观察者时,动态创建这个object的类的子类
对于每个被观察的property,重写其
set方法
在重写的
set方法中调用
- willChangeValueForKey:和
- didChangeValueForKey:通知观察者
当一个property没有观察者时,删除重写的方法
当没有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 |
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] <__NSArrayI 0x8e9aa00>( - (void)setName:(id)arg0 , - (void).cxx_destruct, - (id)name ) // breakpoint 2 (lldb) po [object_getClass(sark) instanceMethods] <__NSArrayI 0x8d55870>( - (void)setName:(id)arg0 , - (class)class, - (void)dealloc, - (BOOL)_isKVOA ) // breakpoint 3 (lldb) po [object_getClass(sark) instanceMethods] <__NSArrayI 0x8e9cff0>( - (void)setName:(id)arg0 , - (void).cxx_destruct, - (id)name ) |
- .cxx_destruct冒出来,这货是个啥?详细的探究请参考我的另一篇文章。
大概就是说arc下这个方法在所有
dealloc调用完成后负责释放所有的变量,当然这个和kvo没啥关系了,回到正题。
从上面breakpoint2的打印可以看出,动态类重写了4个方法:
- setName:最主要的重写方法,set值时调用通知函数
- class隐藏自己必备啊,返回原来类的class
- dealloc做清理犯罪现场工作
- _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]; } @end |
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- 讲解iOS开发中基本的定位功能实现
- iOS中定位当前位置坐标及转换为火星坐标的方法
- js判断客户端是iOS还是Android等移动终端的方法
- iOS应用开发中AFNetworking库的常用HTTP操作方法小结
- iOS应用中UISearchDisplayController搜索效果的用法
- IOS开发环境windows化攻略
- iOS应用中UITableView左滑自定义选项及批量删除的实现
- iOS中UIAlertView警告框组件的使用教程
- 浅析iOS应用开发中线程间的通信与线程安全问题
- 检测iOS设备是否越狱的方法
- .net平台推送ios消息的实现方法
- 探讨Android与iOS,我们将何去何从?
- Android、iOS和Windows Phone中的推送技术详解