isa指针与kvo的关系


前言:类和对象

在面向对象的语言中,对象是根据类创建出来的实例,或者说类是对象的蓝图。基于这个概念,oc有自己的特性,类也是对象,称之为类对象。所以在oc中对象分为以下几类:

  • instance对象 - 实例对象
  • class对象 - 类对象
  • meta-class对象 - 元类对象

所有alloc出来的对象就是instance对象,oc中所有类都是继承自NSObject类

NSObject *obj = [NSObject alloc] init];

obj就是实例对象,NSObject 就是类对象,那么元对象呢?
进入NSObject里看下:

isa指针与kvo的关系_第1张图片

有一个指向Class结构体的isa指针(isa 不就是is kind of的缩写吗?),这个Class就是类对象,我们再进Class里看下:
isa指针与kvo的关系_第2张图片
又有一个指向Class结构体的isa指针,这个Class就是元类对象,结构和类对象一样的,但是存储的信息不一样。手动画了个图表示实例对象,类对象,元类对象的存储。
isa指针与kvo的关系_第3张图片

  • 总结
    1、instance对象的isa指针指向类对象,而类对象的isa指针指向元类对象。
    2、instance对象的对象方法存储在类对象中,而类对象的类方法存储在元类对象中
    3、instance方法的调动是通过isa指针找到class, 然后通过class找到方法去调用,如果找不到则找到superclass 去调用;class方法调用是通过isa指针找到meta-class,然后通过meta-class去找到方法调用,找不到则去superclass找。

正文:isa与kvo

  • kvo的原理
KVO就是键值监听(Key-Value Observing),那么是oc是如何做到对某个对象的属性值进行监听的呢?

先从一段简单的代码来看:

  _oneObject = [[OneObject alloc] init];
  _twoObject = [[OneObject alloc] init];
  [_oneObject addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];
  _oneObject.name = @"now, say my name";
  _twoObject.name = @"hisenberg";
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change context:(void *)context {
    NSLog(@"监听到了%@的%@属性发生了改变%@",object,keyPath,change);
}

_oneobjectname属性进行了KVO,打印出来:

2019-06-10 19:30:40.379500+0800 KVODemo[26423:14306760] 监听到了的name属性发生了改变{
    kind = 1;
    new = "now, say my name";
}

问:由于对_oneObject进行的kvo,所以打印出了它的变化,但是他们都调了父类OneObject的setName:方法,为何只有_oneObject走到了observeValueForKeyPath:ofObject:change:方法呢?

我们把断点打在observeValueForKeyPath:ofObject:change:里,然后在控制台打印一下self._oneObject和_self.twoObject的isa指针:
isa指针与kvo的关系_第4张图片

发现两个对象的类对象不一样,虽然它们都是继承了OneObject,但是由于对self.oneObject进行了kvo,self._oneObject的isa指针却指向了其他的类对象NSKVONotifying_OneObject,看起来格式是NSKVONotifying_XXX的类,很明显这是runtime给OneObject创建的子类。

再补一段代码:

#import "OneObject.h"

@implementation OneObject

- (void)setName:(NSString *)name {
    _name = name;
}

- (void)willChangeValueForKey:(NSString *)key {
   // [super willChangeValueForKey:key];
    NSLog(@"willChangeValueForKey");
}

- (void)didChangeValueForKey:(NSString *)key {
   // [super didChangeValueForKey:key];
    NSLog(@"didChangeValueForKey");
}

@end

如果我们重写willChangeValueForKey和didChangeValueForKey,并且不调super的话,那么断点不会进入observeValueForKeyPath:ofObject:change:的回调方法中,也就是说kvo失效了,如果调用super的话,我们看到控制台的打印:

2019-05-31 14:13:40.249427+0800 KVODemo[96038:9344530] willChangeValueForKey
2019-05-31 14:13:40.249660+0800 KVODemo[96038:9344530] 监听到了的name属性发生了改变{
    kind = 1;
    new = "now, say my name";
}
2019-05-31 14:13:40.249755+0800 KVODemo[96038:9344530] didChangeValueForKey

这就是kvo的原理:

改变对象xxxx的isa指针的指向(isa-swizzing),使其指向类对象的一个子(NSKVONotifying_xxxx),
并重写了属性的set方法,使用willChangeValueForKey和didChangeValueForKey方法并且调用
observeValueForKeyPath:ofObject:change:通知属性的变化。

你可能感兴趣的:(isa指针与kvo的关系)