KVO
Posted coolcold
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了KVO相关的知识,希望对你有一定的参考价值。
官方文档地址:
基本用法:
1. [self.student addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL]
** context作用,用来打标签在回调方法中做区分,应用场景是在如果父类和子类有同一个属性名,回调判断会复杂,如果是用context来区分会很方便,在内存中只能惟一的写一个,相当于一个静态值。而且查找更优越,如果判断类和属性列表的话需要查找他们的缓存列表,所以context的优点是嵌套少,性能好。
2. 改变值 self.person.name = @"person";
3. 响应回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"%@",change);
NSLog(@"downloadProgress == %@",self.person.downloadProgress);
}
4. 析构:
- (void)dealloc{
[self.student removeObserver:self forKeyPath:@"name"];
}
** 如果没有在dealloc中移除观察者,并且被观察对象是一个单例的换,再次打开界面重新给name复制时会闪退或者调用两次等异常。(所以一定要移除,至于是什么导致的崩溃,闭源源码无法看到,猜测是不断添加注册观察者,当系统需要给观察者复制是,原页面已经销毁,找不到原地址,导致野指针崩溃。),下面是官方文档:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if([key isEqualToString:@"name"]){
return YES;
}
return NO;
}
返回NO,则之前添加的观察者,不再回调,也可以选择观察一些特定的key.
如果这里返回NO,也可以使用在set方法里面添加手动观察方法的方式实现添加观察者:
- (void)setName:(NSString *)name{
[self willChangeValueForKey:@"name"];
_name = name;
[self didChangeValueForKey:@"name"];
}
KVO的一个简单应用(为downloadProgress添加观察者,影响下载进度的影响因素包括总的大小和已经写入的大小):
如果是传统的做法需要同时监控两个值,但是使用下面的方法就更便捷一些:
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:@"downloadProgress"]) {
NSArray *affectingKeys = @[@"totalData", @"writtenData"];
keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
}
return keyPaths;
}
上面的代码的主要作用是,当观察的值为downloadProgress时,添加连个影响因子totalData,writtenData,这样当这两个值改变的时候也会回调观察者方法。(联动观察 )
或者是把对象当做观察对象时,对象的属性改变也会调用观察者回调方法。
集合类观察:
如果采用上面的方式观察数组时,如果给数组添加元素,会发现不会调用观察者回调。如果要了解这个原因,就需要了解KVC的内容。(因为集合和简单变量的取值和赋值过程是不一样的)
[[self.student mutableArrayValueForKey:@"dataArray"] addObject:@"hello"];(满足KVC的设置流程)
这样就可以了,必须先初始化数组,接下来分析下原因:
根据之前KVC的官方文档中介绍。self.student是有可能获取不到值的,因为有_key,isKey,key这种变量的存在,赋值的时候不一定会复制到key中,有可能是_key中,所以self.student中不一定有值,所以获取不到。这样的话就需要直接避免这种情况,直接用KVC获取dataArray,这样可以规避KVC查找值导致的问题。
KVO底层原理:
首先验证下是否是观察的set方法:
!-- p.p1>!-- p.p1>!-- p.p1>!-- p.p1>!-- p.p1>!-- p.p1>!-- p.p1>!-- p.p1>以上是关于KVO的主要内容,如果未能解决你的问题,请参考以下文章