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字段作为表的主键,并用它作 ...
随机推荐
- SSM完整项目(内含源码)
SSM 电影后台管理项目 概述 通过对数据库中一张表的CRUD,将相应的操作结果渲染到页面上. 笔者通过这篇博客还原了项目(当然有一些隐藏的坑),然后将该项目上传到了Github.Gitee,在末尾会 ...
- Socket 多任务(多进程/线程、I/O多路复用、事件驱动开发框架)
0. 概述 1. 循环版实现多连接 2. threading.Thread 多线程 3. SockerServer 实现多任务 3.1 ForkingMixIn - 多进程(限 linux) 3.2 ...
- NNLM原理及Pytorch实现
NNLM NNLM:Neural Network Language Model,神经网络语言模型.源自Bengio等人于2001年发表在NIPS上的<A Neural Probabilistic ...
- 源码篇:ThreadLocal的奇思妙想(万字图文)
前言 ThreadLocal的文章在网上也有不少,但是看了一些后,理解起来总感觉有绕,而且看了ThreadLocal的源码,无论是线程隔离.类环形数组.弱引用结构等等,实在是太有意思了!我必须也要让大 ...
- Solon Cloud 分布式开发套件清单与快速概览
Solon Cloud 是一系列的接口标准和配置规范.Solon Cloud 为常见的分布式系统模式提供了一种简单且方便的编程模式,帮助开发人员构建有弹性的.可靠的.协调的应用程序.Solon Clo ...
- 【js】Leetcode每日一题-制作m束花所需的最少天数
[js]Leetcode每日一题-制作m束花所需的最少天数 [题目描述] 给你一个整数数组 bloomDay,以及两个整数 m 和 k . 现需要制作 m 束花.制作花束时,需要使用花园中 相邻的 k ...
- 安装mysql警告: Header V3 DSA/SHA1 Signature, key ID 5072e1f5: NOKEY
CentOS安装rpm安装MySQL时爆出警告: warning: mysql-community-server-5.7.18-1.el6.x86_64.rpm: Header V3 DSA/SHA1 ...
- CentOS7 搭建 Redis 集群
一.手动搭建 1. 准备节点 节点数量至少为 6 个才能保证组成完整高可用的集群 (1) 目录结构 cluster ├── 9001 │ ├── data │ │ ├── appendon ...
- 使用MindSpore的线性神经网络拟合非线性函数
技术背景 在前面的几篇博客中,我们分别介绍了MindSpore的CPU版本在Docker下的安装与配置方案.MindSpore的线性函数拟合以及MindSpore后来新推出的GPU版本的Docker编 ...
- Junit单元测试&反射&注解
内容索引 1. Junit单元测试 2. 反射 3. 注解 Junit单元测试: * 测试分类: 1. 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值. 2. 白盒测试:需要写代码的.关 ...