OC——关于KVO

我们知道在WPF、Silverlight中都有一种双向绑定机制,如果数据模型修改了之后会立即反映到UI视图上,类似的还有如今比较流行的基于MVVM设计模式的前端框架,例如Knockout.js。其实在ObjC中原生就支持这种机制,它叫做Key Value Observing(简称KVO)。KVO其实是一种观察者模式,利用它可以很容易实现视图组件和数据模型的分离,当数据模型的属性值改变之后作为监听器的视图组件就会被激发,激发时就会回调监听器自身。在ObjC中要实现KVO则必须实现NSKeyValueObServing协议,不过幸运的是NSObject已经实现了该协议,因此几乎所有的ObjC对象都可以使用KVO。

在ObjC中使用KVO操作常用的方法如下:

  • 注册指定Key路径的监听器: addObserver: forKeyPath: options:  context:
  • 删除指定Key路径的监听器: removeObserver: forKeyPathremoveObserver: forKeyPath: context:
  • 回调监听: observeValueForKeyPath: ofObject: change: context:

如何使用KVO呢?

首先,你要为你想观察的对象添加一个观察者代码如下:

[object addObserver: observer forKeyPath: @"frame" options: 0 context: nil];

调用方法是:

object : 被观察对象

observer: 观察对象

forKeyPath里面带上property的name,如UIView的frame、center等等

options: 有4个值,分别是:

NSKeyValueObservingOptionNew 把更改之后的值提供给处理方法

NSKeyValueObservingOptionOld 把更改之前的值提供给处理方法

NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。

NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和值改变之后。

注:例子里的0就代表不带任何参数进去

context: 可以带入一些参数,任何类型都可以,强制转就可以。

接下来观察到值的变化后该应该调用相关的方法来处理,这个方法是:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

其中:

keyPath: 对应forKeyPath

  object:  被观察的对象

  change:  对应options里的NSKeyValueObservingOptionNew、NSKeyValueObservingOptionOld等

  context: 对应context

示例代码

头文件

 1 #import <Foundation/Foundation.h>
 2 
 3 @interface Student : NSObject
 4 @property (nonatomic)NSString *name;
 5 
 6 @property (nonatomic)NSString *courseName;
 7 
 8 @property (nonatomic)double age;
 9 
10 - (void)changeCourseName:(NSString *)newCourseName;
11 @end

实现文件

 1 #import "Student.h"
 2 
 3 @implementation Student
 4 
 5 - (id)init
 6 {
 7     if(self = [super init])
 8     {
 9         
10         [self addObserver:self
11               forKeyPath:@"courseName"
12               options:NSKeyValueObservingOptionNew
13          |NSKeyValueObservingOptionOld
14                 context:nil];
15         [self addObserver:self
16               forKeyPath:@"age"
17                  options:NSKeyValueObservingOptionNew
18          |NSKeyValueObservingOptionOld
19                  context:nil];
20     }
21     return self;
22 }
23 
24 - (void)changeCourseName:(NSString *)newCourseName{
25     
26     _courseName = newCourseName;
27      
28 }
29 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
30 {
31     if([keyPath isEqualToString:@"courseName"])
32     {
33         NSLog(@"课程发生了改变");
34         NSLog(@"新课程是:%@  老课程是:%@",[change objectForKey:@"new"],[change objectForKey:@"old"]);
35 
36     }
37     else if([keyPath isEqualToString:@"age"])
38     {
39         NSLog(@"年龄发生了改变");
40         NSLog(@"新年龄是:%@  老年龄是:%@",[change objectForKey:@"new"],[change objectForKey:@"old"]);
41     }
42 }
43 #pragma mark 重写销毁方法
44 -(void)dealloc//注意一定要移除监听
45 {
46     //[super dealloc];//注意启用了ARC,此处不需要调用
47     [self removeObserver:self forKeyPath:@"courseName"];
48     [self removeObserver:self forKeyPath:@"age"];
49 
50 }
51 @end

调用函数

#import <Foundation/Foundation.h>
#import "Student.h"
#import "pageView.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Student *student = [[Student alloc] init];
        student.courseName = @"math";
        student.age = 10;
        student.courseName = @"ddd";
        student.age = 15;
        
    }
    return 0;
}

输出结果

2015-07-10 14:59:13.074 TestKVO[1649:1396305] 课程发生了改变

2015-07-10 14:59:13.076 TestKVO[1649:1396305] 新课程是:math  老课程是:<null>

2015-07-10 14:59:13.076 TestKVO[1649:1396305] 年龄发生了改变

2015-07-10 14:59:13.076 TestKVO[1649:1396305] 新年龄是:10  老年龄是:0

2015-07-10 14:59:13.076 TestKVO[1649:1396305] 课程发生了改变

2015-07-10 14:59:13.076 TestKVO[1649:1396305] 新课程是:ddd  老课程是:math

2015-07-10 14:59:13.077 TestKVO[1649:1396305] 年龄发生了改变

2015-07-10 14:59:13.077 TestKVO[1649:1396305] 新年龄是:15  老年龄是:10

 

原文地址:https://www.cnblogs.com/zhangjingyangjinjin/p/4635768.html