对于KVO以前只是会用,并不了解其内部实现。最近对KVO内部实现进行探究,在此做个总结。
先看代码和现象:
1.添加继承于NSObject的OBJ类,并且为OBJ类添加一个days属性
#import
@interface OBJ : NSObject
@property (nonatomic,assign) int days;
@property (nonatomic,assign) int years;
@end
在OBJ.m中添加func1 和 func2两个方法
#import "OBJ.h"
@implementation OBJ
- (void)func1 {
}
- (void)func2 {
}
@end
2.在viewcontroller的viewdidload方法中分别打印出为OBJ实例添加监听前后的方法列表和实例对象的所属类
#import "ViewController.h"
#import "OBJ.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
OBJ *obj = [OBJ new];
//输出obj对象所属的类
NSLog(@"%s",class_getName(object_getClass(obj)));
//给obj对象添加监听
[obj addObserver:self forKeyPath:@"days options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@""];
//输出被监听后obj对象所属的类
NSLog(@"%s",class_getName(object_getClass(obj)));
unsigned int count;
//获取添加监听后的obj对象的方法列表(其实是obj所属类的方法列表 实例是没有方法列表的 具体原因可以看此篇文章OC方法列表和方法调用的总结)
Method *method_List = class_copyMethodList(object_getClass(obj), &count);
for (int i = 0 ; i < count ; i ++) {
Method method = method_List[i];
const char* name = sel_getName(method_getName(method));
int arguments = method_getNumberOfArguments(method);
NSLog(@"方法名:%@,参数个数:%d",[NSString stringWithUTF8String:name],
arguments);
}
//给obj对象移除监听
[obj removeObserver:self forKeyPath:@"days"];
//输出移除监听后的obj对象所属的类
NSLog(@"%s",class_getName(object_getClass(obj)));
//获取移除监听后obj对象的方法列表
method_List = class_copyMethodList(object_getClass(obj), &count);
for (int i = 0 ; i < count ; i ++) {
Method method = method_List[i];
const char* name = sel_getName(method_getName(method));
int arguments = method_getNumberOfArguments(method);
//const char* encoding = method_getTypeEncoding(method);
NSLog(@"方法名:%@,参数个数:%d",[NSString stringWithUTF8String:name],
arguments);
}
结果如下:
============= 添加监听前 ==========
当前类: OBJ
父类: NSObject
方法名:func1,参数个数:2
方法名:func2,参数个数:2
方法名:setDays:,参数个数:3
方法名:days,参数个数:2
方法名:years,参数个数:2
方法名:setYears:,参数个数:3
============= 添加监听后 ==========
当前类: NSKVONotifying_OBJ
父类: OBJ
方法名:setDays:,参数个数:3
方法名:class,参数个数:2
方法名:dealloc,参数个数:2
方法名:_isKVOA,参数个数:2
============= 移除监听后 ==========
当前类: OBJ
父类: NSObject
方法名:func1,参数个数:2
方法名:func2,参数个数:2
方法名:setDays:,参数个数:3
方法名:days,参数个数:2
方法名:years,参数个数:2
方法名:setYears:,参数个数:3
由结果可以看出:
1.添加监听后 系统自动为我们生成了一个新类(NSKVONotifying_OBJ)。并且,生成的新类继承于OBJ类。新生类(NSKVONotifying_OBJ)中重写了setDays方法(只重写被监听属性的set方法),所以在我们重新给所监听的属性赋值时,会调用新类的set方法。这是我们就可以在新类的set方法中做消息的发送
2.添加监听后对象所属的类发生了改变(对象所属类是对象isa指针指向的类,所以此时对象的isa指针由指向OBJ类 改为指向新生成的NSKVONotifying_OBJ类)
3.移除监听后对象(obj)的isa重新指向原类(OBJ)
思考:
为什么新生成类要继承于原类?可以做到只重写被监听属性的set方法 而不改变其他方法的实现与调用。
为什么对象的isa指针改为指向新生成类?当被监听属性值改变时会调用新生成类的set方法,而不是调用原类的属性的set方法,在set方法中进行消息转发等操作,并且不会影响原有类。