前言:类和对象
在面向对象的语言中,对象是根据类创建出来的实例,或者说类是对象的蓝图。基于这个概念,oc
有自己的特性,类也是对象,称之为类对象。所以在oc
中对象分为以下几类:
-
instance
对象 - 实例对象 -
class
对象 - 类对象 -
meta-class
对象 - 元类对象
所有alloc出来的对象就是instance对象,oc中所有类都是继承自NSObject类
NSObject *obj = [NSObject alloc] init];
obj
就是实例对象,NSObject
就是类对象,那么元对象呢?
进入NSObject里看下:
有一个指向Class结构体的isa指针(isa 不就是is kind of的缩写吗?),这个Class就是类对象,我们再进Class里看下: 又有一个指向Class结构体的isa指针,这个Class就是元类对象,结构和类对象一样的,但是存储的信息不一样。手动画了个图表示实例对象,类对象,元类对象的存储。
- 总结
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);
}
对_oneobject的name属性进行了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指针:发现两个对象的类对象不一样,虽然它们都是继承了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:通知属性的变化。