KVO概述
键值观察Key-Value-Observer
就是观察者模式。利用一个key
来找到某个属性并监听其值的改变。KVO
建立在 KVC
之上,它能够观察一个对象的 KVC key path
值的变化。
KVO
提供一种机制,指定一个被观察的对象(A类
),当对象某个属性(A类
中的属性name
)发生更改时,对象会获得通知,并作出相应处理。且不需要给A类
添加任何额外代码,我称之为偷窥
。
特点
观察者观察的是属性,只有遵循KVO
变更属性值的方式才会执行KVO
的回调方法,例如是否执行了setter
方法。如果赋值没有通过setter
方法或者KVC
,而是直接修改属性对应的成员变量,例如:仅调用_name = @"newName"
,这时是不会触发KVO
机制,更加不会调用回调方法的。所以使用KVO
机制的前提是遵循KVO
的属性设置方式来变更属性值。
应用
KVO的应用分为三个步骤
1.注册监听者,开始偷窥
2.在回调方法中处理属性发生的变化
3.移除监听者
注册监听者
KVO机制由NSKeyValueObserving协议提供支持,NSObject遵守了该协议,该协议包含了如下常用方法用于注册监听器
//anObserver:观察者
//keyPath:要观察的属性
//options:给你观察键值变化的选择
//context:方便传输你需要的数据
- (void)addObserver:(NSObject *)anObserver
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context
监听方法(回调方法)
当属性变化时,激发监听方法
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
由此可见,作为监听器的组件需要重写该方法
移除监听者
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
小小小Demo
新建工程先
新建文件父类NSObject
KVO.h
@interface KVO : NSObject
@property (nonatomic , assign) int num;
@end
ViewController.h中
@interface ViewController : UIViewController
@property (nonatomic , strong) UILabel* label;
@property (nonatomic , strong) UIButton* button;
@property (nonatomic , strong) KVO* myKvo;
@end
ViewController.m中
//
// ViewController.m
// KVO
//
// Created by 王旭 on 2020/8/13.
// Copyright © 2020 王旭. All rights reserved.
//
#import "ViewController.h"
#import "KVO.h"
//#import <objc/runtime.h>
@interface ViewController ()
@property (nonatomic , strong) KVO* myKvo;
/*1.注册对象myKvo为被观察者: option中, NSKeyValueObservingOptionOld 以字典的形式提供 “初始对象数据”; NSKeyValueObservingOptionNew 以字典的形式提供 “更新后新的数据”; */
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor yellowColor];
_label = [[UILabel alloc] init];
_label.frame = CGRectMake(200, 200, 150, 30);
_label.font = [UIFont systemFontOfSize:20];
_label.text = 0;
_button = [[UIButton alloc] init];
_button.frame = CGRectMake(170, 500, 100, 40);
_button.backgroundColor = [UIColor grayColor];
[_button addTarget:self action:@selector(changeNum) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_label];
[self.view addSubview:_button];
self.myKvo = [KVO new];
[self.myKvo addObserver:self forKeyPath:@"num" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"123"];
// NSLog(@"%s",object_getClassName(self.myKvo));
/* 2.只要object的keyPath属性发生变化,就会调用此回调方法,进行相应的处理:UI更新:*/
}
- (void) changeNum
{
//KVC方法setValue
// [self.myKvo setValue:@"20" forKey:@"num"];
//set方法
// [self.myKvo setNum:20];
//点语法
self.myKvo.num = 20;
}
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
// 判断是否为self.myKVO的属性“num”:
if(context ==@"123" && object == self.myKvo)
{
// 响应变化处理:UI更新(label文本改变)
NSLog(@"old num:%@ new num:%@",[change valueForKey:@"old"], [change valueForKey:@"new"]);
}
}
- (void) dealloc
{
[_myKvo removeObserver:self forKeyPath:@"num"];
}
@end