KVO
KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变.
基本应用
在ViewController中监听Person对象的age变化,点击控制器屏幕就改变age值,可以看到收到变化通知.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.p1 = [[Person alloc] init];
self.p1.age = 10;
[self.p1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"age change"];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.p1.age = 12;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"%@",change);
}
点击屏幕可以看到打印
2019-05-07 21:49:11.190686+0800 KVO[38048:2769151] {
kind = 1;
new = 12;
old = 10;
}
KVO探究
添加KVO之前的Person
在添加监听前断点查看Person对象
添加KVO之前
添加KVO之后查看Person对象
添加KVO之后比对添加KVO添加前后发现,Person对象的类信息发生了改变.p1由Person对象变成了NSKVONotifying_Person对象.
打印下KVO前后方法列表变化
- (NSString *)getMethodList:(Class )cls{
unsigned int count;
Method *methods = class_copyMethodList(cls, &count);
NSString *names = @"";
for (int i = 0 ; i < count; i ++) {
Method method = methods[i];
NSString *name = NSStringFromSelector(method_getName(method));
names = [NSString stringWithFormat:@"%@_%@",names,name];
}
return names;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.p1 = [[Person alloc] init];
self.p1.age = 10;
NSLog(@"KVO之前-->%@",[self getMethodList:object_getClass(self.p1)]);
[self.p1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"age change"];
NSLog(@"KVO之后-->%@",[self getMethodList:object_getClass(self.p1)]);
}
打印结果:
2019-05-07 22:23:09.775530+0800 KVO[39211:2801239] KVO之前-->_setAge:_age
2019-05-07 22:23:09.775838+0800 KVO[39211:2801239] KVO之后-->_setAge:_class_dealloc__isKVOA
多了三个方法:
1.class:这个也间接证明为啥用实例对象获取class会总是Person
2.dealloc
3.isKVOA
内部实现类似这样:
(void)setAge:(int)age
{
_NSSetIntValueAndNotify();
}
// 屏幕内部实现,隐藏了NSKVONotifying_Person类的存在
- (Class)class
{
return [Person class];
}
- (void)dealloc
{
// 收尾工作
}
- (BOOL)_isKVOA
{
return YES;
}
所以对比两个类的结构:
两个类结构对比.png
总结
- 利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类
- 当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数
1.willChangeValueForKey:
2.父类原来的setter
3.didChangeValueForKey:
内部会触发监听器(Oberser)的监听方法(observeValueForKeyPath:ofObject:change:context:)
网友评论