【基本概念】
键值观察是一种使对象获取其他对象的特定属性变化的通知机制。控制器层的绑定技术就是严重依赖键值观察获得模型层和控制器层的变化通知的。对于不依赖控制器层类的应用程序,键值观察提供了一种简化的方法来实现检查器并更新用户界面值。
与NSNotification不同,键值观察并没有所谓的中心对象来为所有观察者提供变化通知。取而代之的,当有变化发生时,通知被直接发送至处于观察状态的对象。NSObject提供这种基础的键值观察实现方法,你几乎不用重写该方法。
你可以观察任意对象属性,包括简单属性,对一或是对多关系。对多关系的观察者将会被告知发生变化的类型-也就是任意发生变化的对象。键值观察为所有对象提供自动观察兼容性。你可以通过禁用自动观察通知并实现手动通知来筛选通知。
【注册观察者】
为了正确接收属性的变更通知,观察者必须首先发送一个addObserver:forKeyPath:options:context:消息至被观察对象,用以传送观察对象和需要观察的属性的关键路径,以便与其注册。选项参数指定了发送变更通知时提供给观察者的信息。使用NSKeyValueObservingOptionOld选项可以将初始对象值以变更字典中的一个项的形式提供给观察者。指定NSKeyValueObservingOptionNew选项可以将新的值以一个项的形式添加至变更字典。你可以使用逐位“|”这两个常量来指定接受上述两种类型的值。
下面我通过几个实例来带大家实现一下KVO的通知机制:
【实例一】对基本数据类型的属性进行观察
(1)我建立的是一个基于OC的iOS项目,在storyboard中拖入一个按钮,并在ViewController中使用IBAction实现按钮点击。
(2)声明一个int类型的属性在ViewController.m文件中:
- @interface ViewController ()
- @property(assign,nonatomic) int count;
- @end
(3)然后实现一个初始化方法,初始化count的值:
- - (instancetype)init
- {
- self = [super init];
- if (self) {
- self.count = 1;
- }
- return self;
- }
(4)在按钮点击响应中进行自加运算,以后我就监听点击按钮count数值改变的这个事件:
- - (IBAction)clicked:(id)sender {
- self.count++;
- }
(5)在viewDidLoad方法中注册观察事件:
- - (void)viewDidLoad {
- [super viewDidLoad];
- [self addObserver:self forKeyPath:@"count" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
- }
(6)最后重写一个方法observeValueForKeyPath;如下:
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(voidvoid *)context{
- if ([keyPath isEqual: @"count"]) {
- NSLog(@"count = %d",self.count);
- }
- }
(7)输出结果如下:每当我点击按钮,观察事件就会调用注册的方法。
。
【实例二】对 对象进行观察
(1)在上述的基础上,新建一个Other类,在h头文件中声明一个变量:
- #import <Foundation/Foundation.h>
- @interface Other : NSObject
- @property(assign,nonatomic) int number;
- @end
(2)在Other.m文件中实现初始化方法,初始化number变量:
- #import "Other.h"
- @implementation Other
- - (instancetype)init
- {
- self = [super init];
- if (self) {
- self.number = 100;
- }
- return self;
- }
- @end
(3)在ViewController.m中实现如下:
- #import "ViewController.h"
- #import "Other.h"
- @interface ViewController ()
- @property(strong,nonatomic) Other *other;
- @end
- @implementation ViewController
- - (void)viewDidLoad {
- [super viewDidLoad];
- //监听Other类;
- self.other = [[Other alloc] init];
- [self.other addObserver:self forKeyPath:@"number" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
- }
- - (IBAction)clicked:(id)sender {
- _other.number++;
- }
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(voidvoid *)context{
- //我假设能被2整除时输出,注意我使用的是keyPath进行匹配;
- if ([keyPath isEqual: @"number"] && _other.number % 2 == 0){
- NSLog(@"other.number = %d",_other.number);
- }
- }
- @end
(4)输出结果如下:
。
【实例三】同时对多个对象进行观察
在上述【实例一】【实例二】的基础上,我同时对两个变量进行观察,其他类中的实现不变,ViewController.m中的实现如下:
- #import "ViewController.h"
- #import "Other.h"
- @interface ViewController ()
- @property(assign,nonatomic) int count;
- @property(strong,nonatomic) Other *other;
- @end
- @implementation ViewController
- - (void)viewDidLoad {
- [super viewDidLoad];
- [self addObserver:self forKeyPath:@"count" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
- //监听Other类;
- self.other = [[Other alloc] init];
- [self.other addObserver:self forKeyPath:@"number" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
- }
- - (instancetype)init
- {
- self = [super init];
- if (self) {
- self.count = 1;
- }
- return self;
- }
- - (IBAction)clicked:(id)sender {
- _other.number++;
- self.count++;
- }
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(voidvoid *)context{
- if (object == self.other) {
- NSLog(@"number = %d",_other.number);
- }
- else if (object == self && self.count % 2 == 0){
- NSLog(@"count = %d",self.count);
- }
- }
- @end
输出结果如下:
。
【实例四】使用change参数实时观察参数变化
ViewController.m中的实现如下:
- #import "ViewController.h"
- #import "Other.h"
- @interface ViewController ()
- @property(assign,nonatomic) int count;
- @property(strong,nonatomic) Other *other;
- @end
- @implementation ViewController
- - (void)viewDidLoad {
- [super viewDidLoad];
- [self addObserver:self forKeyPath:@"count" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
- }
- - (instancetype)init
- {
- self = [super init];
- if (self) {
- self.count = 1;
- }
- return self;
- }
- - (IBAction)clicked:(id)sender {
- _other.number++;
- self.count++;
- }
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(voidvoid *)context{
- //在这里对观察变量进行打印;
- NSLog(@"%@",change);
- }
- @end
输出结果如下: