说说背景,研究下面的代码时,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)的结果竟会不一致?的更多相关文章

  1. VB.NET中使用Linq TO SQL添加数据后获得自增长列ID

    VB.NET中使用Linq TO SQL添加数据后获得自增长列ID: Dim tempOrdre As New Order With { .CustomerID = cmbCustomerName.S ...

  2. __getattribute__(self, obj) 这个方法中的obj这个参数

    class Itcast(object): def __init__(self, subject1): self.subject1 = subject1 print("^^^^^^^---- ...

  3. 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: ' ...

  4. MSSQL2005后版本插入数据返回ID的新写法

    例子: INSERT VolunteerSound_Table (Title,ArticleContent)OUTPUT Inserted.ID VALUES ('FirstVal','bbbbb') ...

  5. 使用mybatis插入自增主键ID的数据后返回自增的ID

    在开发中碰到用户注册的功能需要用到用户ID,但是用户ID是数据库自增生成的,这种情况上网查询后使用下面的方式配置mybatis的insert语句可以解决: <insert id="in ...

  6. DedeCMS清空删除所有文档后新建文档信息ID从1开始

    方法一.登录织梦后台,找到系统->系统设置->SQL命令行工具 分别运行以下命令: 清除表中的数据,删除所有文章: truncate table `dede_arctiny`; trunc ...

  7. Mybatis 插入后返回数据库自动增长ID

    MySQL和MSSQL返回主键方法 在personMap.xml中 <insert id="addPerson" parameterType="orm.Person ...

  8. java中String.valueOf(obj)、(String)obj与obj.toString()有什么区别

    方法1:采用 Object.toString()方法 在这种使用方法中,因为java.lang.Object类里已有public方法.toString(),所以对任何严格意义上的java对象都可以调用 ...

  9. [转] Java 插入表记录后得到自增的id (附3种方法代码)

    转自:https://blog.csdn.net/yaerfeng/article/details/7231093 在MySQL中,使用auto_increment类型的id字段作为表的主键,并用它作 ...

随机推荐

  1. Django 入门范例

    1. Django 介绍 2. Django 环境搭建 3. 模型(Model) 4. 站点管理 5. 视图(View) 6. 模板(Template) 1. Django 介绍 MVC 模型 大部分 ...

  2. PHP版DES算法加密数据

    php7之后的版本 php的mcrypt 扩展已经过时了大约10年,并且用起来很复杂.因此它被废弃并且被 OpenSSL 所取代. 从PHP 7.2起它将被从核心代码中移除并且移到PECL中.PHP手 ...

  3. 【Redis连接超时】记录线上RedisConnectionFailureException异常排查过程

    项目架构: 部分组件如下: SpringCloudAlibaba(Nacos+Gateway+OpenFeign)+SpringBoot2.x+Redis 问题背景: 最近由于用户量增大,在高峰时期, ...

  4. 病毒木马查杀实战第024篇:MBR病毒之编程解析引导区

    前言 通过之前的学习,相信大家已经对磁盘的引导区有了充分的认识.但是我们之前的学习都是利用现成的工具来对引导区进行解析的,而对于一名反病毒工程师而言,不单单需要有扎实的逆向分析功底,同时也需要有很强的 ...

  5. Redhat中网络启动错误解决办法( Failed to start LSB: Bring up/down networking RTNETLINK answers: File exists)

    关于Redhat系列中网络启动失败的解决办法 报错: Failed to start LSB: Bring up/down networking.             RTNETLINK answ ...

  6. 声明:songzijian这个域名已经被抢注。大家别上了。不是我了。

    声明:songzijian这个域名已经被抢注.大家别上了.不是我了.

  7. 【转】浅谈自动特征构造工具Featuretools

    转自https://www.cnblogs.com/dogecheng/p/12659605.html 简介 特征工程在机器学习中具有重要意义,但是通过手动创造特征是一个缓慢且艰巨的过程.Python ...

  8. Instagram 为什么不用redis

    Hi 我还是大粽子 碎碎念 让我比较兴奋的就是这段时间的文章,被感兴趣的同学一一关注,关注量上涨就是我的最大动力. 我每周都会输出至少3篇原创文章,希望能被更多的同学关注,点赞,在看,形成习惯. In ...

  9. 这次我好像才真的明白了CSS Rem字体计算的原理

    背景 如何按照设计稿中标注的尺寸,直接写页面的样式,不再需要px2rem这样的工具或者人工转换 ? 只要你明白了rem的计算原理,这个问题的答案超级简单. 根字体大小计算核心原理 设备的根字体大小 * ...

  10. Base64文件上传(Use C#)

    Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,它是一种基于64个可打印字符来表示二进制数据的方法. 使用base64进行文件上传的具体流程是:前台使用js将文件转换为base64格 ...