博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
KVO本质的推导
阅读量:6113 次
发布时间:2019-06-21

本文共 4490 字,大约阅读时间需要 14 分钟。

阅读过很多文章都只是阐述了KVO的本质,并没有辅以代码推导过程,所以本文的主要目的是梳理正向推导本质的过程

本文主要分为两部分

  1. 正向推导KVO本质
  2. 无法推导的进行反向验证

KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变

KVODemoClass 测试类

@interface KVODemoClass : NSObject@property (assign, nonatomic) int number;@property (assign, nonatomic) int reference; // 参照属性 无用@end复制代码

我们创建两个实例对象 对 demo1 这个对象number属性进行键值监听, demo2 不做处理

此时进入lldb 分别打印出两个对象的isa指针如下

(lldb) p self.demo1->isa(Class) $1 = NSKVONotifying_KVODemoClass(lldb) p self.demo2->isa(Class) $2 = KVODemoClass复制代码

通过isa指针我们可以看出进行键值监听的 demo1 的实际指向已经变成 NSKVONotifying_KVODemoClass 而我们知道一个实例对象的isa指针指向的应该是其类对象,由此我们可以得出使用KVO后Runtime创建一个 NSKVONotifying_原类名 的派生类,而我们在使用的时候是毫无感知的。

那么 NSKVONotifying_KVODemoClass 的内部情况是怎么样的呢?

我们来输出一下NSKVONotifying_KVODemoClass和KVODemoClass类对象中的方法列表和属性列表 由于本文重点在KVO 所以runtime的知识点就不赘述了,直接附上代码

- (void)printMethodNamesOfClass:(Class)cls{    unsigned int count;    unsigned int propertyCount;    Method *methodList = class_copyMethodList(cls, &count);    objc_property_t *propertyList = class_copyPropertyList(cls, &propertyCount);       NSMutableString *methodNames = [NSMutableString string];      for (int i = 0; i < count; i++) {              Method method = methodList[i];              NSString *methodName = NSStringFromSelector(method_getName(method));            [methodNames appendString:methodName];        [methodNames appendString:@", "];    }      NSMutableString *propertyNames = [NSMutableString string];        for (int i = 0; i < propertyCount; i++) {               objc_property_t ivar = propertyList[i];              NSString *propertyName = [[NSString alloc] initWithCString:property_getName(ivar)];        [propertyNames appendString:propertyName];        [propertyNames appendString:@", "];    }        free(methodList);    free(propertyList);    NSLog(@"%@ %@", cls, methodNames);    NSLog(@"%@ %@", cls, propertyNames);}复制代码

结果如下

KVODemo[4935:1065888] NSKVONotifying_KVODemoClass setNumber:, class, dealloc, _isKVOA,KVODemo[4935:1065888] NSKVONotifying_KVODemoClassKVODemo[4935:1065888] KVODemoClass setReference:, reference, number, setNumber:,KVODemo[4935:1065888] KVODemoClass number, reference,复制代码

派生类没有产生新的属性而是对set方法进行了重写,并且没有被监听的reference属性的set方法不会重写。

那么被重写的set方法有什么变化呢?

NSLog(@"添加监听之前%p------%p",[self.demo1 methodForSelector:@selector(setNumber:)],[self.demo2 methodForSelector:@selector(setNumber:)]);        NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;    [self.demo1 addObserver:self forKeyPath:@"number" options:options context:@"123"];        NSLog(@"添加监听之后%p------%p",[self.demo1 methodForSelector:@selector(setNumber:)],[self.demo2 methodForSelector:@selector(setNumber:)]);复制代码

打个断点进入lldb

KVODemo[5093:1124218] 添加监听之前0x10a5c2a70------0x10a5c2a70KVODemo[5093:1124218] 添加监听之后0x10a9736c4------0x10a5c2a70(lldb) p (IMP)0x10a5c2a70(IMP) $0 = 0x000000010a5c2a70 (KVODemo`-[KVODemoClass setNumber:] at KVODemoClass.m:12)(lldb) p (IMP)0x10a9736c4(IMP) $1 = 0x000000010a9736c4 (Foundation`_NSSetIntValueAndNotify)复制代码

很神奇的发现添加监听后 setNumber方法的实际指向已经变成了 NSSetIntValueAndNotify 函数 当然这是由于我们的属性类型是int 附上一张图

不同类型的属性对应不同的函数。

由于技术原因本菜的正向推导只能进行到这一步了,下面内容是反向验证,也希望有大神能给予正向推导的思路

验证内容:

  • 1.NSSetIntValueAndNotify中方法的调用为
    • 1.1 willChangeValueForKey
    • 1.2 setNumber
    • 1.3 didChangeValueForKey
    • 1.4 在1.3的调用过程中会调用监听者的
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary
*)change context:(void *)context复制代码

重写KVODemoClass的以下方法

-(void)setNumber:(int)number{    _number = number;    NSLog(@"setNumber:");}- (void)willChangeValueForKey:(NSString *)key{    NSLog(@"willChangeValueForKey---begin");    [super willChangeValueForKey:key];    NSLog(@"willChangeValueForKey---end");}- (void)didChangeValueForKey:(NSString *)key{    NSLog(@"didChangeValueForKey---begin");    [super didChangeValueForKey:key];    NSLog(@"didChangeValueForKey---end");}复制代码

控制台输出为

KVODemo[5265:1173234] willChangeValueForKey---beginKVODemo[5265:1173234] willChangeValueForKey---endKVODemo[5265:1173234] setNumber:KVODemo[5265:1173234] didChangeValueForKey---beginKVODemo[5265:1173234] 监听到
的number属性值改变了 - { kind = 1; new = 2; old = 1;} - 123KVODemo[5265:1173234] didChangeValueForKey---end复制代码
  • 2.单独调用didChangeValueForKey不会触发监听

我们注释掉willChangeValueForKey的实现,控制台输出

KVODemo[5317:1175253] setNumber:KVODemo[5317:1175253] didChangeValueForKey---beginKVODemo[5317:1175253] didChangeValueForKey---end复制代码

监听方法没有被调用

小记:之前一直在简书,写的文章也多为随笔,随手记录不成文。这篇是真正意义上第一次系统的写文章,诸多不足请各位见谅~多提宝贵意见!也希望自己能坚持写下去~

转载地址:http://sznka.baihongyu.com/

你可能感兴趣的文章
【随笔】工程师都是性情中人
查看>>
[译] React v16.8: 含有Hooks的版本
查看>>
“寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
查看>>
现代 JavaScript 函数库 usuallyjs 的安装和使用
查看>>
Leaflet-Develop-Guide
查看>>
Android Studio 导入 AOSP 源码
查看>>
React16时代,该用什么姿势写 React ?
查看>>
小程序上传图片到七牛云(支持多张上传,预览,删除)
查看>>
3分钟学会如何调度运营海量Redis系统
查看>>
浅析MySQL事务中的redo与undo
查看>>
iOS开发助手、ipa便捷上传工具!
查看>>
一文了解腾讯云数据库SaaS服务
查看>>
再见,BLE的那些坑!
查看>>
前端杂谈: 如何实现一个 Promise?
查看>>
一个超便捷的豆瓣电影Chrome插件
查看>>
C++ 学习笔记之——字符串和字符串流
查看>>
antd图标的本地化使用
查看>>
Vuejs 实战观书有感 C1
查看>>
UnixBench算分介绍
查看>>
GO的部分总结~
查看>>