zoukankan      html  css  js  c++  java
  • 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) 返回的结果不一致的原因。

    在北京的灯中,有一盏是我家的。这个梦何时可以实现?哪怕微微亮。北京就像魔鬼训练营,有能力的留,没能力的走……
  • 相关阅读:
    sysbench压测Oracle
    linux使用FIO测试磁盘的iops
    oracle 列授权相关测试
    11G、12C安装结束需要做的一些操作
    Oracle 12C R2 on Linux 7.X 单实例静默安装文档
    auto_create_partition
    关于使用索引的一些经验
    转:【Python3网络爬虫开发实战】 requests基本用法
    转:http协议学习系列(响应头---Response Headers)
    关于建立老猿Python研学群的公告
  • 原文地址:https://www.cnblogs.com/huangzs/p/14849564.html
Copyright © 2011-2022 走看看