KVO-键值观察
KVC:对象取值或者设置值。
KVO:监听对象值的变化。
响应式编程的一种。
KVO的使用非常简单,使用KVO的要求是对象必须能支持kvc机制——所有NSObject的子类都支持这个机制。
KVO观察的实际是属性的setter方法,成员变量的改变不能被观察到。
KVO的实现原理如下
在调用addObserver方法的时候,实现了以下步骤
1,利用runtime动态创建当前类的子类。
2,重写子类的setter方法,并在内部回复父类的做法。
3,动态修改当前类的类型,让他变成子类的类型。(以后调用setter方法,实际是调用子类的setter方法)。
1,简单的例子,观察person类的name属性的变化
@interface ViewController ()
@property (nonatomic, strong) PersonModel * person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"load");
self.person = [[PersonModel alloc]init];
//添加观察者
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
}
//观察回调
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"%@",change);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
static int a = 0;
self.person.name = [NSString stringWithFormat:@"%d",a++];
}
//移除观察者
-(void)dealloc{
[self.person removeObserver:self forKeyPath:@"name"];
}
@end
2,手动触发KVO,在需要的时候才触发,默认是自动触发
在需要手动触发的类重写类方法。
关闭自动触发
#import "PersonModel.h"
@implementation PersonModel
//是否自动触发观察key的改变
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
//name属性手动触发,其他属性自动触发
if([key isEqualToString:@"name"]){
return NO;
}
return YES;
}
@end
手动触发
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
static int a = 0;
//即将触发
[self.person willChangeValueForKey:@"name"];
self.person.name = [NSString stringWithFormat:@"%d",a++];
//已经触发
[self.person didChangeValueForKey:@"name"];
}
3,类的属性是对象时添加观察的方式。
person中包含了一个dog对象,观察dog的age。
//添加观察者
[self.person addObserver:self forKeyPath:@"dog.age" options:NSKeyValueObservingOptionNew context:nil];
4,数组,字典等容器类,观察不到,因为没有setter方法。但是可以利用KVO观察容器属性的变化
实际原理是重写了集合的添加方法,删除方法,替换方法,原理和重写setter方法是一样的。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//利用KVC获取到arr,注意这里使用的是mutableArrayValueForKey,数组直接初始化数据可以不用KVC。
NSMutableArray *arr = [self.person mutableArrayValueForKey:@"arr"];
//KVC可以监听到数组的添加,删除,替换
[arr addObject:@"11"];
}
5,添加一个类的子类的多个属性观察,注意这里子类前要有一个_,也可以直接添加多个观察
VC.m
[self.person addObserver:self forKeyPath:@"dog" options:NSKeyValueObservingOptionNew context:nil];
PersonModel.m
//返回要观察的字符串
+(NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
NSSet *keypath = [NSSet set];
if([key isEqualToString:@"dog"]){//子类属性名
keypath = [keypath setByAddingObjectsFromArray:@[@"_dog.name",@"_dog.age"]];
}
return keypath;
}