KVO简介
KVO<NSKeyValueObserving>,是一个非正式协议,提供了一个途径,使对象(观察者)能够观察其他对象(被观察者)的属性,当被观察者的属性发生变化时,观察者就会被告知该变化。由NSKeyValueObserving协议提供支持,NSObject类继承了该协议,所以NSObject的子类都可使用该方法。
基本方法
添加观察者
/*
object : 被观察对象
observer: 观察对象
forKeyPath里面带上property的name,如UIView的frame、center,如自定义对象的成员变量等等
options: 有4个值,分别是:
NSKeyValueObservingOptionNew 把更改之后的值提供给处理方法
NSKeyValueObservingOptionOld 把更改之前的值提供给处理方法
NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。
NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和值改变之后。
context: 可以带入一些参数,其实这个挺好用的,任何类型都可以,自己强转就好了
*/
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context
实现观察者回调方法
/*
keyPath: 对应forKeyPath
object: 被观察的对象
change: 对应options里的NSKeyValueObservingOptionNew、NSKeyValueObservingOptionOld等
context: 对应context
*/
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary*)change context:(nullable void *)context
移除观察者
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
KVO模式
KVO分为自动和手动两种模式
简单的实现一个自动模式
//自动模式
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc] init];
person.name = @"A";
//NSLog(@"class:%s",object_getClassName(person));
NSLog(@"before:%@",[self findSubClass:[person class]]);
[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
NSLog(@"before:%@",[self findSubClass:[person class]]);
//NSLog(@"class:%s",object_getClassName(person));
person.name = @"B";
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%@",change);
}
/*
利用runtime获取该类的所有子类
*/
- (NSArray *)findSubClass:(Class)defaultClass {
int count = objc_getClassList(NULL,0);
if (count <= 0) {
return [NSArray array];
}
NSMutableArray *output = [NSMutableArray arrayWithObject:defaultClass];
Class *classes = (Class *)malloc(sizeof(Class) * count);
objc_getClassList(classes, count);
for (int i = 0; i < count; i++) {
if (defaultClass == class_getSuperclass(classes[i])) {
[output addObject:classes[i]];
}
}
return output;
}
实现一个手动模式,首先要明白KVO自动模式的实现原理:
如果原类为 Person,那么会自动生成一个继承Person类的派生类,名为 NSKVONotifying_Person (见自动模式截图),系统会偷偷将原类Person的isa 指针指向动态生成的派生类,在给被监控属性赋值时执行的其实是派生类的 setter 方法,该派生类的setter方法中会增加willChangeValueForKey和didChangeValueForKey方法。
//手动模式
/*
需要在被观察者对象(这里被观察者是Pesron类)中重写automaticallyNotifiesObserversForKey静态方法
在这里,我仅针对成员变量name进行手动
*/
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if ([key isEqualToString:@"name"]) {
return NO;
}
return YES;
}
/*
手动模式就需要手动调用willChangeValueForKey和didChangeValueForKey两个方法
*/
- (void)setName:(NSString *)name {
[self willChangeValueForKey:@"name"];
_name = name;
[self didChangeValueForKey:@"name"];
}
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc] init];
person.name = @"A";
[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
person.name = @"B";
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%@",change);
NSLog(@"class:%@",[object class]);
}