关于KVO,估计很多同学对KVO一样是很模糊的,都听到大家在说,面试会问,但是在开发中很少使用到,一般使用到的也是某个组件中封装使用,而这个封装又不是交由自己来完成,因此还是很模糊。
现在想来,似乎我也不清楚了,到底KVO是什么,怎么用,又会在哪种场景中使用。带着疑问,让我们一起来学习学习吧
1、什么是KVO?
KVO其实就是Key-Value-Observer,也就是键值观察者,是基于KVC机制实现的另一种模式。也许有不少同学用过SVPullToRefresh或者MJRefresh刷新组件,都使用了KVO来实现。
2、使用场景会使用KVO呢?
似乎使用场景并不多,NSOperationQueue中使用了KVO,当NSOperation对象状态变化(finished,canceled等)时,观察者可以知道变化,从而做出相应的处理(移除已完成的等等);
3、怎么实现KVO?
现在假设需求:某个学生成绩变化时,让观察者知道
让我们建立一个学生实体类,实现文件什么也不用动:
@interface HYBStudent : NSObject
@property (nonatomic, copy) NSString *studentName;
@property (nonatomic, assign) CGFloat grade;
@end
下面viewcontroller中实例化一个学生实体,让controller成为学生实体的观察者,这里是使用一对一的观察模式,看看打印结果:
#import "ViewController.h"
#import "HYBStudent.h"
@interface ViewController ()
@property (nonatomic, strong) HYBStudent *student;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.student = [[HYBStudent alloc] init];
self.student.studentName = @"李四";
self.student.grade = 90.f;
[self.student addObserver:self forKeyPath:@"grade" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial context:nil];
[self performSelector:@selector(changeGrade) withObject:nil afterDelay:4.0];
}
- (void)changeGrade {
self.student.grade = 100.0f;
}
- (void)observeValueForKeyPath:(nullable NSString *)keyPath
ofObject:(nullable id)object
change:(nullable NSDictionary *)change
context:(nullable void *)context {
if ([keyPath isEqualToString:@"grade"]) {
NSLog(@"student %@'s grade is %f'",
self.student.studentName, self.student.grade);
}
}
- (void)dealloc {
[self.student removeObserver:self forKeyPath:@"grade" context:nil];
}
@end
打印结果如下:
2015-08-28 16:18:00.537 KVODEMO[1281:476192] student 李四's grade is 90.000000'
2015-08-28 16:18:04.539 KVODEMO[1281:476192] student 李四's grade is 100.000000'
下面我们来看看另一种场景:一对多
需求如下:学生李四的成绩变化时,要通知他的同学张三
实现如下,在HYBStudent.m文件中实现如下方法:
- (void)observeValueForKeyPath:(nullable NSString *)keyPath
ofObject:(nullable id)object
change:(nullable NSDictionary *)change
context:(nullable void *)context {
if ([keyPath isEqualToString:@"grade"]) {
NSLog(@"张三的同学成绩发生了变化: %@'s grade is %f'",
((HYBStudent *)object).studentName, ((HYBStudent *)object).grade);
}
}
然后在viewDidLoad的最后追加如下代码,其它不变:
self.zhangStudent = [[HYBStudent alloc] init];
self.zhangStudent.studentName = @"张三";
self.zhangStudent.grade = 59;
[self.student addObserver:self.zhangStudent
forKeyPath:@"grade"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial
context:nil];
看看打印结果 :
2015-08-28 16:43:51.234 KVODEMO[1317:480175] student 李四's grade is 90.000000'
2015-08-28 16:43:51.235 KVODEMO[1317:480175] 张三的同学成绩发生了变化: 李四's grade is 90.000000'
2015-08-28 16:43:55.236 KVODEMO[1317:480175] 张三的同学成绩发生了变化: 李四's grade is 100.000000'
2015-08-28 16:43:55.237 KVODEMO[1317:480175] student 李四's grade is 100.000000'
总结:1、谁成为观察者,谁就得实现
- (void)observeValueForKeyPath:(nullable NSString *)keyPath
ofObject:(nullable id)object
change:(nullable NSDictionary *)change
context:(nullable void *)context;
这个API,以便在值发生变化时,做相应的处理,如果没有实现此API,会抛出异常,导致crash。2、添加观察者与移除观察者是成对出现的,一定要记得移除
[self.student removeObserver:self forKeyPath:@"grade" context:nil];
[self.student removeObserver:self.zhangStudent forKeyPath:@"grade" context:nil];