KVO也就是观察者模式(Key Value Observer),KVO可以用于观察对象某个属性的改变,当被观察的属性的值发生变化后会自动的调用对应的方法。例如tableview的位置改变的时候导航栏的透明度的变化情况,亦或者是微博的点赞功能等都可以借鉴KVO。
使用KVO大体的步骤可分为:注册观察者,重写observeValueForKeyPath:ofObject:change:context:这个方法,然后销毁观察者。这个销毁观察者这一步个人感觉很重要,因为如果不销毁的话,这会造成内存泄漏。下面就用个例子说明下KVO。
1. 注册成为观察者
上面已经说到了能观察对象的属性,所以我们先创建一个对象
// 创建对象
model = [[WJModel alloc]init];
model.likes = @10;
model.love = 20;
这里需要观察的是对象的likes
和love
值的变化。下面就进行注册观察者:
[model addObserver:self forKeyPath:@"likes" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
[model addObserver:self forKeyPath:@"love" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
注:
参数1:表示的谁是观察者
参数2:表明观察属性的路径
参数3:被观察的值发生变化的时候需要获取的值,这里可以观察到新值和旧值。
参数4:上下文关系
2.重写方法
重写方法的时候可以获取值改变的时刻,以及拿到新值与旧值。而且当被观察的属性发生变化的时候会自动调用这个方法。所以这个方法可能回被调用多次。<其实这也是基于OC的runtime的运行机制的,会在里面的子类里实现setter方法>。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
NSLog(@"change:%@",change);
NSLog(@"%@",change[@"new"]);
// 拿到新值和旧值就可以进行一些操作了。
}
注:
参数1 keyPath 被观察的属性的路径
参数2 object 被观察的对象
参数3 change 发生改变的值(新值或者旧值)
由于在其他地方设置了一个点击事件,可以更改属性的值,
- (IBAction)change:(UIButton *)sender {
int value = arc4random() % 150;
self.model.likes = [NSNumber numberWithInt:value];
self.model.love = value + 78;
}
控制台的打印提示:
likes的打印日志:
// 第一次点击
change:{
kind = 1;
new = 140;
old = 10;
}
// 第二次点击
change:{
kind = 1;
new = 13;
old = 140;
}
love的点击日志:
// 第一次点击
change:{
kind = 1;
new = 21;
old = 20;
}
// 第二次点击
change:{
kind = 1;
new = 22;
old = 21;
}
这两次点击就可以看出可以得知观察属性变化是成功了的,而且这个方法也是调用了多次的。
3. 销毁观察者
使用玩了观察着需要把观察者移除,这样可以有效的避免掉内存移除的问题,从而避免了系统的崩溃。
移除的方法和添加的方法一样,也是需要keyPath的。
- (void)dealloc {
[model removeObserver:self forKeyPath:@"likes"];
[model removeObserver:self forKeyPath:@"love"];
}
这样使用KVO模式的几个步骤就完成了。
优点:
KVO适合任何对象监听另一个对象的改变,这是一个对象与另外一个对象保持同步的一种方法。KVO只能对属性做出反应,不会用来对方法或者动作做出反应。
1.提供一个简单地方法来实现两个对象的同步。
2.能对不是我们创建的对象做出反应。
3.能够提供被观察属性的新值和旧值。
4.由于用了keypaths 来观察属性,因此也可以观察嵌套对象。
对比:
观察属性的改变还有就是可以用到通知中心或者是协议代理。
1.通知中心NotificationCenter:
这是一个单例对象,允许当事件发生的时候通知一些对象,满足控制器与一个任意的对象进行通信的目的,这种模式的基本特征就是接收到在该controller中发生某种事件而产生的消息,controller用一个key(通知名称,与keyPath相似),这样对于controller是匿名的,其他的使用同样地key来注册了该通知的对象也能对通知的事件作出反应。
2.代理
代理就不多做描述了,都知道怎么用。
当处理属性层的消息的事件时候,使用KVO,其他的尽量使用代理,除非代码需要处理的东西确实很简单,那么用通知。
总结:
1.使用KVO需要注意的是观察的对象属性,一般的基本类型的数据是没有办法进行观察的。
2.使用观察者的步骤大体来说就是三步:注册观察者及添加需要观察的属性,实现相关的方法,注销观察者对象。
3.由于keyPath的值是String类型定义的,编译器不会检查也不会出现警告,所以添加和注销的时候keyPath的值须一致,不然添加观察和销毁观察会失败。