iOS开发之快速上手KVO

前言

写这篇博客的原因是昨晚需要用到KVO,只是要用最简单的KVO,观察一个属性,在其改变时得到通知,然而到网上找了许多博客,看了许多文章,都是长篇大论介绍了一大堆什么是KVO,然而看完我还是一脸蒙逼,我只是想要快速部署,你跟我说那么多干嘛,最后还是自己写了一个Demo跑通了,这里就写一篇博客,记录一下如何部署一个最简单的KVO,让有同样需求的人能够在最短的时间里部署KVO。

正文

前期准备

首先说一下部署KVO需要准备什么:

  • 一个被观察者
  • 一个被观察的属性
  • 一个观察者

当然被观察者和观察者可以是同一个,我下面的例子中观察者和被观察者都是一个ViewController。

具体步骤

1.被观察者调用这个方法注册一个观察者:

1
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

感觉这个API很难用,刚看到的时候根本不知道后面两个参数该传入什么,Mattt Thompson是这样吐槽这个方法的:让这个API不堪入目的事实就是最后两个参数经常是 0 和 NULL。

2.观察者实现下面这个方法,当观察的值改变了,此方法就会被调用:

1
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;

3.移除观察者

1
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

一个最简单的实现

上面的方法具体怎么用我都没解释,但是怎么解释都没有一个例子来得直接,下面我们就上代码,talk is cheap, show you the code!

我们新建一个Single View Application,然后就在这个默认的VC里操作。

首先加入一个属性

1
@property (nonatomic, assign) NSInteger count;

然后在viewDidLoad中注册观察者。

1
[self addObserver:self forKeyPath:@"count" options:NSKeyValueObservingOptionNew context:nil];

context的值可以直接设为nil

然后实现观察者的方法,在这个方法里可以做一些刷新UI的工作

1
2
3
4
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%li", (long)self.count);
self.label.text = [NSString stringWithFormat:@"%li", (long)self.count];
}

然后点击button的时候修改属性值,这里要注意的是一定要用点方法赋值,而不能直接修改属性对应的成员变量,比如此处改为_count++KVO就会失效,也不要重写该属性的setter方法,不然也可能引起失效。因为自动合成的setter方法是使用KVC中的方法:- (void)setValue:(nullable id)value forKey:(NSString *)key,KVC(键-值编码)是KVO的基础 后面经过测试发现重写setter方法不会使KVO失效,即使你在setter方法中什么也不做。而- (void)setValue:(nullable id)value forKey:(NSString *)key 这个方法是会去调用setter方法的,如果没有setter方法,会去寻找实例变量,并修改其值,而且通过这个方法也是一定会出发KVO的。

1
2
3
- (void)buttonClicked:(UIButton *)sender {
self.count++;
}

最后最重要的一点,取消注册。当一个观察者完成一个监听需要取消移除这个观察者。通常这个动作是在-dealloc-observeValueForKeyPath: ofObject: change: context:完成的,这里就是在-dealloc完成的

1
2
3
- (void)dealloc {
[self removeObserver:self forKeyPath:@"count"];
}

至此一个最简单的KVO就完成了,其实很简单,而且这种简单的模式在很多时候都已经够用了,如果需要这个demo的代码可以去我github下载。

注意

使用KVO时需要注意几个问题:

1.添加观察者时注意key不能出错,不然会崩溃
2.完成监听一定要取消注册,不然程序可能也会崩溃
3.移除监听时也可能会出现崩溃,如果重复移除或移除一个你没有注册的监听

对于1和3中的问题,如果实在没法避免可以用try catch来包裹。

总结

KVO中值得挖掘的东西很多,这只是一篇十分浅薄的记录,希望能够帮助大家,后面有机会还会深入学习KVO,最后让我膜拜一下Mattt Thompson,他的文章真是有深度,能够学到很多,本文就参考了他的文章。

本文参考了如下文章:

Key-Value Observing