什么是KVO
- KVO俗称“键值监听”,用来监听某个对象属性值的改变
KVO的使用
- (void)viewDidLoad {
[super viewDidLoad];
self.person1 = [[GMPerson alloc] init];
self.person1.height = 160;
self.person1.name = @"GM";
NSKeyValueObservingOptions option = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
/**
* Observer: 观察者
* forKeyPath: 需要观察的属性
* options: 有4个值 分别是:
* NSKeyValueObservingOptionNew 更改后的值,提供给处理方法
* NSKeyValueObservingOptionOld 更改前的值,提供给处理方法
* NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值
* NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和值改变之后
* context: 可以带入一些自定义参数
*/
[self.person1 addObserver:self forKeyPath:@"name" options:option context:@"123"];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// self.person1.height = 175;
self.person1.name = @"GMhaha";
}
- (void)dealloc{
[self.person1 removeObserver:self forKeyPath:@"name"];
}
/** 监听当前对象属性值发生改变时会调用 */
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"监听到了%@的属性值%@ 改变了 - %@ 自定义参数 - %@",object,keyPath,change,context);
}
KVO 本质
在给某个对象添加KVO监听时,程序会使用Runtime动态创建一个类,例如:以上代码,我们要给 self.person1 这个对象添加KVO,在注册监听时,系统会使用Runtime动态的创建一个GMPerson的子类 NSKVONotifying_GMPerson。self.person1 这个对象的isa指针,指向 NSKVONotifying_GMPerson 这个子类,在我们改变person1里面的属性值时,会调用 NSKVONotifying_GMPerson 这个子类里的 set方法。NSKVONotifying_GMPerson 类里的set方法会调用Foundation框架里的 _NSSetIntValueAndNotify(根据具体属性类型调用。比如:float 类型会调用_NSSetFloatValueAndNotify) 这个C语言方法 这个方法里面实际上又调用了 willChangeValueForKey、set方法、didChangeValueForKey 方法,didChangeValueForKey方法里实际上是调用了通知监听器的方法。
NSKVONotifying_GMPerson 类具体实现的伪代码如下:
//
// NSKVONotifying_GMPerson.m
// KVO
//
// Created by G_M on 2022/2/10.
//
#import "NSKVONotifying_GMPerson.h"
@implementation NSKVONotifying_GMPerson
- (void)setHeight:(int)height{
_NSSetIntValueAndNotify();
}
/** 伪代码,具体实现苹果未开源 */
void _NSSetIntValueAndNotify(){
[self willChangeValueForKey:@"height"];
[super setHeight:height];
[self didChangeValueForKey:@"height"];
}
- (void)didChangeValueForKey:(NSString *)key{
/** 通知监听器,某属性的key值发生了改变 */
[oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}
@end
这些都是苹果动态生成的,并不需要我们自己编写,写出来只是为了帮助理解KVO的本质。