KVO后[obj class]与object_getClass(id obj)的结果竟会不一致?
说说背景,研究下面的代码时,KVO后[obj class]与object_getClass(id obj)的结果竟会不一致?
PersonModel *aPersonModel = [[PersonModel alloc] init];
aPersonModel.name=@"lisi";
NSLog(@"之前%@ %@",[aPersonModel class],object_getClass(aPersonModel));
[aPersonModel addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
NSLog(@"之后%@ %@",[aPersonModel class],object_getClass(aPersonModel));
aPersonModel.name=@"zhangsan"; //[aPersonModel removeObserver:self forKeyPath:@"name"];
查看 NSObject 底层代码
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
很明显,实例方法 class 内部调用和 object_getClass 毫无区别,都是获得对象的 isa 指针。类方法直接返回的本身。那么为啥 KVO 后[obj class]与object_getClass(id obj)的结果竟会不一致?
打印一下 KVO 后,NSKVONotifying_PersonModel 里的方法。发现系统内部,重写了 class 方法,直接返回的 KVO 之前的类对象。
Class cls = object_getClass(aPersonModel);
[self printMethodList:cls];
- (void)printMethodList:(Class)cls
{ unsigned int outCount;
Method* methods = class_copyMethodList(cls,&outCount); for (int i = 0; i < outCount ; i++)
{
SEL name = method_getName(methods[i]);
NSString *strName = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
NSLog(@"selName : %@",strName);
}
}
结果打印:
2021-06-04 14:40:02.434062+0800 MsgSendTest[70021:7491894] selName : setName:
2021-06-04 14:40:02.434233+0800 MsgSendTest[70021:7491894] selName : class
2021-06-04 14:40:02.434368+0800 MsgSendTest[70021:7491894] selName : dealloc
2021-06-04 14:40:02.434487+0800 MsgSendTest[70021:7491894] selName : _isKVOA
重新回归KVO的原理:
1.比如原先实例 aPersonModel 的isa指针指向的是 PersonModel,那么当你在第一次调用过
[aPersonModel addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
方法后,runtime 会创建一个新的类,类名以NSKVONotifing开头叫 NSKVONotifying_PersonModel,同时更改实例 aPersonModel 的 isa 的指针,将其指向 NSKVONotifying_PersonModel 。
2.在 NSKVONotifying_PersonModel 中重写观察的属性 name 的 setter 方法 setName ,并在它里面调用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
方法。这样当改变属性 name 的值时,外面就会得到通知。
3.在 NSKVONotifying_PersonModel 中重写 - (Class) class 方法,返回原先的 isa 指向的类(在这个例子中就是 PersonModel )。这就是为什么[aPersonModel class]与object_getClass(aPersonModel) 返回的结果不一致的原因。
KVO后[obj class]与object_getClass(id obj)的结果竟会不一致?的更多相关文章
- VB.NET中使用Linq TO SQL添加数据后获得自增长列ID
VB.NET中使用Linq TO SQL添加数据后获得自增长列ID: Dim tempOrdre As New Order With { .CustomerID = cmbCustomerName.S ...
- __getattribute__(self, obj) 这个方法中的obj这个参数
class Itcast(object): def __init__(self, subject1): self.subject1 = subject1 print("^^^^^^^---- ...
- R = [obj for obj in recs[imagename] if obj['name'] == classname] KeyError: '007765'
在用RFBNet做测试的时候,好几次总是遇到 R = [obj for obj in recs[imagename] if obj['name'] == classname] KeyError: ' ...
- MSSQL2005后版本插入数据返回ID的新写法
例子: INSERT VolunteerSound_Table (Title,ArticleContent)OUTPUT Inserted.ID VALUES ('FirstVal','bbbbb') ...
- 使用mybatis插入自增主键ID的数据后返回自增的ID
在开发中碰到用户注册的功能需要用到用户ID,但是用户ID是数据库自增生成的,这种情况上网查询后使用下面的方式配置mybatis的insert语句可以解决: <insert id="in ...
- DedeCMS清空删除所有文档后新建文档信息ID从1开始
方法一.登录织梦后台,找到系统->系统设置->SQL命令行工具 分别运行以下命令: 清除表中的数据,删除所有文章: truncate table `dede_arctiny`; trunc ...
- Mybatis 插入后返回数据库自动增长ID
MySQL和MSSQL返回主键方法 在personMap.xml中 <insert id="addPerson" parameterType="orm.Person ...
- java中String.valueOf(obj)、(String)obj与obj.toString()有什么区别
方法1:采用 Object.toString()方法 在这种使用方法中,因为java.lang.Object类里已有public方法.toString(),所以对任何严格意义上的java对象都可以调用 ...
- [转] Java 插入表记录后得到自增的id (附3种方法代码)
转自:https://blog.csdn.net/yaerfeng/article/details/7231093 在MySQL中,使用auto_increment类型的id字段作为表的主键,并用它作 ...
随机推荐
- Django 入门范例
1. Django 介绍 2. Django 环境搭建 3. 模型(Model) 4. 站点管理 5. 视图(View) 6. 模板(Template) 1. Django 介绍 MVC 模型 大部分 ...
- PHP版DES算法加密数据
php7之后的版本 php的mcrypt 扩展已经过时了大约10年,并且用起来很复杂.因此它被废弃并且被 OpenSSL 所取代. 从PHP 7.2起它将被从核心代码中移除并且移到PECL中.PHP手 ...
- 【Redis连接超时】记录线上RedisConnectionFailureException异常排查过程
项目架构: 部分组件如下: SpringCloudAlibaba(Nacos+Gateway+OpenFeign)+SpringBoot2.x+Redis 问题背景: 最近由于用户量增大,在高峰时期, ...
- 病毒木马查杀实战第024篇:MBR病毒之编程解析引导区
前言 通过之前的学习,相信大家已经对磁盘的引导区有了充分的认识.但是我们之前的学习都是利用现成的工具来对引导区进行解析的,而对于一名反病毒工程师而言,不单单需要有扎实的逆向分析功底,同时也需要有很强的 ...
- Redhat中网络启动错误解决办法( Failed to start LSB: Bring up/down networking RTNETLINK answers: File exists)
关于Redhat系列中网络启动失败的解决办法 报错: Failed to start LSB: Bring up/down networking. RTNETLINK answ ...
- 声明:songzijian这个域名已经被抢注。大家别上了。不是我了。
声明:songzijian这个域名已经被抢注.大家别上了.不是我了.
- 【转】浅谈自动特征构造工具Featuretools
转自https://www.cnblogs.com/dogecheng/p/12659605.html 简介 特征工程在机器学习中具有重要意义,但是通过手动创造特征是一个缓慢且艰巨的过程.Python ...
- Instagram 为什么不用redis
Hi 我还是大粽子 碎碎念 让我比较兴奋的就是这段时间的文章,被感兴趣的同学一一关注,关注量上涨就是我的最大动力. 我每周都会输出至少3篇原创文章,希望能被更多的同学关注,点赞,在看,形成习惯. In ...
- 这次我好像才真的明白了CSS Rem字体计算的原理
背景 如何按照设计稿中标注的尺寸,直接写页面的样式,不再需要px2rem这样的工具或者人工转换 ? 只要你明白了rem的计算原理,这个问题的答案超级简单. 根字体大小计算核心原理 设备的根字体大小 * ...
- Base64文件上传(Use C#)
Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,它是一种基于64个可打印字符来表示二进制数据的方法. 使用base64进行文件上传的具体流程是:前台使用js将文件转换为base64格 ...