说说背景,研究下面的代码时,KVO后[obj class]与object_getClass(id obj)的结果竟会不一致?

  1. PersonModel *aPersonModel = [[PersonModel alloc] init];
  2. aPersonModel.name=@"lisi";
  3. NSLog(@"之前%@ %@",[aPersonModel class],object_getClass(aPersonModel));
  4. [aPersonModel addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
  5. NSLog(@"之后%@ %@",[aPersonModel class],object_getClass(aPersonModel));
  6. aPersonModel.name=@"zhangsan";
  7. //[aPersonModel removeObserver:self forKeyPath:@"name"];

 

查看 NSObject 底层代码

  1. + (Class)class {
  2. return self;
  3. }
  4. - (Class)class {
  5. return object_getClass(self);
  6. }

 

  1. Class object_getClass(id obj)
  2. {
  3. if (obj) return obj->getIsa();
  4. else return Nil;
  5. }

 

很明显,实例方法 class 内部调用和 object_getClass 毫无区别,都是获得对象的 isa 指针。类方法直接返回的本身。那么为啥 KVO 后[obj class]与object_getClass(id obj)的结果竟会不一致?

 

打印一下 KVO 后,NSKVONotifying_PersonModel 里的方法。发现系统内部,重写了 class 方法,直接返回的 KVO 之前的类对象。

  1. Class cls = object_getClass(aPersonModel);
  2. [self printMethodList:cls];

 

  1. - (void)printMethodList:(Class)cls
  2. {
  3. unsigned int outCount;
  4. Method* methods = class_copyMethodList(cls,&outCount);
  5. for (int i = 0; i < outCount ; i++)
  6. {
  7. SEL name = method_getName(methods[i]);
  8. NSString *strName = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
  9. NSLog(@"selName : %@",strName);
  10. }
  11. }

 

结果打印:

  1. 2021-06-04 14:40:02.434062+0800 MsgSendTest[70021:7491894] selName : setName:
  2. 2021-06-04 14:40:02.434233+0800 MsgSendTest[70021:7491894] selName : class
  3. 2021-06-04 14:40:02.434368+0800 MsgSendTest[70021:7491894] selName : dealloc
  4. 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) 返回的结果不一致的原因。

 

版权声明:本文为huangzs原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/huangzs/p/14849564.html