IOS学习和总结KVO

关于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;

  // add observer to the student
  [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 {
  // Don't forget to remove observer
  [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的最后追加如下代码,其它不变:

  // 再次实例化一个学生实体,作为student的同学
  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、添加观察者与移除观察者是成对出现的,一定要记得移除

  // Don't forget to remove observer
  [self.student removeObserver:self forKeyPath:@"grade" context:nil];
  [self.student removeObserver:self.zhangStudent forKeyPath:@"grade" context:nil];
上一篇:sdutOJ 查字典(Python)


下一篇:io_uring 社区开发报告 - 2021.1