一,概述
KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。
二,使用方法
系统框架已经支持KVO,所以程序员在使用的时候非常简单。
1. 注册,指定被观察者的属性,
为了正确接收属性的变更通知,观察对象必须首先发送一个addObserver:forKeyPath:options:context:
消息至被观察对象,用以传送观察对象和需要观察的属性的关键路径,以便与其注册。选项参数指定了发送变更通知时提供给观察者的信息。使用NSKeyValueObservingOptionOld
选项可以将初始对象值以变更字典中的一个项的形式提供给观察者。指定NSKeyValueObservingOptionNew
选项可以将新的值以一个项的形式添加至变更字典。你可以使用逐位OR
这两个常量来指定接收上述两种类型的值。
[self.modelA addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
当你注册一个对象为观察者时,还可以同时指定一个内容指针。当observeValueForKeyPath:ofObject:change:context:
被调用时,内容指针会被提交至观察者。该指针可为C语言指针或对象引用。该指针可作为指定被观察变更的唯一标示符,或是为观察者提供其他数据。该指针不会被保留,应用程序本身应保证该指针在观察方象被取消观察者身份之前被释放。
2. 实现回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
当对象的一个被观察属性发生变动时,观察者收到一个observeValueForKeyPath:ofObject:change:context:
消息。所有观察者都必须实现这一方法。触发观察通知的对象和键路径、包含变更细节的字典,以及观察者注册时提交的上下文指针均被提交给观察者。
变更字典项NSKeyValueChangeKindKey
供发生变更的类型信息。如果被观察对象的值改变了,NSKeyValueChangeKindKey
项将返回一个NSKeyValueChangeSetting
。依赖观察者注册时指定的选项,字典中的NSKeyValueChangeOldKey
和NSKeyValueChangeNewKey
项分别包含了被观察属性变更前后的值。
如果被观察的属性是一个对多的关系,NSKeyValueChangeKindKey
项同样可以通过返回三个不同的项NSKeyValueChangeInsertion
,NSKeyValueChangeRemoval
或NSKeyValueChangeReplacement
来分别指明关系中的对象被执行的插入、删除和替换操作。
变更字典项NSKeyValueChangeIndexesKey
是一个指定关系表变更索引的NSIndexSet
对象。注册观察者时,如果NSKeyValueObservingOptionNew
或NSKeyValueObservingOptionOld
被指定为选项,变更字典中NSKeyValueChangeOldKey
和NSKeyValueChangeNewKey
两个项将以数组形式包含相关对象在变更前后的值。
3. 移除观察
你可以发送一条指定观察方对象和键路径的removeObserver:forKeyPath:
消息至被观察的对象,来移除一个键-值观察者。
[self.modelA removeObserver:self forKeyPath:@"name"];
例子:
新建一个工程,建立Model
#import <Foundation/Foundation.h> @interface C_Model : NSObject @property (nonatomic, strong) NSString* name; @property (nonatomic, assign) int age; @end
在Demo.M中
#import "C_ViewController.h" #import "C_Model.h" @interface C_ViewController () @property (nonatomic,strong) C_Model* modelA; -(void)press; @end @implementation C_ViewController - (void)viewDidLoad { [super viewDidLoad]; UIButton* btn=[UIButton buttonWithType:UIButtonTypeRoundedRect]; btn.frame = CGRectMake(100, 100, 100, 50); [btn addTarget:self action:@selector(press) forControlEvents:UIControlEventTouchUpInside]; [btn setTitle:@"Name" forState:UIControlStateNormal]; [self.view addSubview:btn]; self.view.backgroundColor=[UIColor whiteColor]; // self.modelA=[[C_Model alloc]init]; [self.modelA setValue:@"abc" forKey:@"name"]; [self.modelA setValue:[NSNumber numberWithInt:20] forKey:@"age"]; //建立观察者 [self.modelA addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; } //KVO的回调方法 //keyPath,改变的值,即响应回调方法的值 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { NSString* str = [change objectForKey:NSKeyValueChangeNewKey]; //keyPath改变后的新值 NSLog(@"change new :%@",str); //keyPath改变后的旧值 str = [change objectForKey:NSKeyValueChangeOldKey]; NSLog(@"change old :%@",str); if ([keyPath isEqualToString:@"name"]) { NSLog(@"name : %@",[self.modelA valueForKey:@"name"]); } } - (void)press { //[self.modelA setValue:@"vvv" forKey:@"name"]; self.modelA.name=@"fff"; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)dealloc { [super dealloc]; [self.modelA removeObserver:self forKeyPath:@"name"]; } @end