KVO介绍:
KVO
,全称为Key-Value observing
,中文名为键值观察
,KVO是一种机制,它允许将其他对象的指定属性的更改通知给对象。
KVO的使用1(KeyPath):
1.添加观察者
[self.dog addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];
2.监听观察者
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if ([keyPath isEqualToString:@"name"]) {
NSLog(@"%@",change);
}
}
3.移除观察者:(必须一一对应,添加了,再释放的时候就需要移除)
[self.dog removeObserver:self forKeyPath:@"name" context:NULL];
KVO的使用2(context):
//定义context
static void *PersonNickContext = &PersonNickContext;
static void *PersonNameContext = &PersonNameContext;
//注册观察者
[self.person addObserver:self forKeyPath:@"nick" options:NSKeyValueObservingOptionNew context:PersonNickContext];
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:PersonNameContext];
//KVO回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if (context == PersonNickContext) {
NSLog(@"%@",change);
}else if (context == PersonNameContext){
NSLog(@"%@",change);
}
}
KVO的自动触发和手动触发:
// 自动触发
+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key{
return YES;
}
//手动触发
+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key{
return NO;
}
- (void)setName:(NSString *)name{
//手动开关
[self willChangeValueForKey:@"name"];
_name = name;
[self didChangeValueForKey:@"name"];
}
KVO观察多个属性:
//1、合二为一的观察方法
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:@"wallet"]) {
NSArray *affectingKeys = @[@"rmb", @"dollars"];
keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
}
return keyPaths;
}
//2、注册KVO观察
[self.person addObserver:self forKeyPath:@"wallet" options:(NSKeyValueObservingOptionNew) context:NULL];
//3、触发属性值变化
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.person.dollars += 1;
self.person.rmb += 1;
}
//4、移除观察者
- (void)dealloc{
[self.person removeObserver:self forKeyPath:@"wallet"];
}
KVO观察可变数组:
kvo观察机制是基于 setValue 之上的,而可变数组在进行 addObject、removeObject等操作的时候,并不会触发 setValue 的方法,故此kvo会失效,这时候我们需要改进,在取我们的数组的时候要进行另一种方法去取
[[self mutableArrayValueForKey:@"dataSource"] addObject:@"ss"];
**KVO观察属性,但不会观察成员变量
属性比成员变量多一个set 和 get 方法,所以在改变其值的时候会触发kvo,但是成员变量并不会触发kvo
KCO底层实现原理探究:
1.注册KVO观察者后,观察对象的isa指针指向会发生改变
2.派生出来的类会重写原有类的set方法
3.当set方法监听到新值的变化,则回调监听,将监听结果告知观察者
4.移除观察者时,isa指针会重新指向原有类
5.派生类并不会随着移除操作而销毁,会一直存在于内存中
代码探究原理
- 注册观察者之前:实例对象person的isa指针指向LGPerson
-
注册观察者之后:实例对象person的isa指针指向NSKVONotifying_LGPerson
-
移除观察者之后:实例对象的isa指向更改为LGPerson类