什么是KVO
KVO全称Key Value Observing
,是苹果提供的一套事件通知机制。允许对象监听另一个对象特定属性的改变,并在改变时接收到事件。由于 KVO 的实现机制,只针对属性才会发生作用,一般继承自 NSObject 的对象都默认支持 KVO。
KVO的用法
使用KVO大概分为三个步骤
注册观察者
注册观察者的方法为addObserver:forKeyPath:options:context:
,这个方法的四个参数作用为:
1.observer:观察者。
2. keyPath:被观察者,即为观察的属性,名字要和声明的一致
3. options:配置KVO机制,有下面四个参数。
1、NSKeyValueObservingOptionNew 把更改之前的值提供给处理方法
2、NSKeyValueObservingOptionOld 把更改之后的值提供给处理方法
3、NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。
4、NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和值改变之后。
4. context: 可以是任意类型的参数,可以传值
[_aKvo addObserver:self forKeyPath:@"kvoString" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
实现回调方法
回调方法为 observeValueForKeyPath:ofObject:change:context:
,当被观察的属性发生变化时,KVO机制会调用次方法来通知观察者。
// 设置按钮触发监听
- (void)pressChange {
NSDictionary *aDictionary = [NSDictionary dictionaryWithObjectsAndKeys:_kvoTextField.text,@"kvoString", nil];
[_aKvo setValuesForKeysWithDictionary:aDictionary];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"kvoString"]) {
NSLog(@"old nameWord: %@", [change objectForKey:@"old"]);
NSLog(@"new nameWord: %@", [change objectForKey:@"new"]);
}
}
在结束监听后,移除KVO
调用removeObserver:forKeyPath:
方法将KVO移除。需要注意的是,调用removeObserver需要在观察者消失之前,否则会导致Crash.
- (void)dealloc {
[_aKvo removeObserver:self forKeyPath:@"kvoString"];
}
以下是demo的完整代码
创建一个类继承NSObject,里面声明一个被监听的属性
// KVO监听
//
// Created by 差不多先生 on 2021/8/4.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Kvo : NSObject
@property (nonatomic, strong) NSString* kvoString;
@end
NS_ASSUME_NONNULL_END
ViewController
#import <UIKit/UIKit.h>
#import "message.h"
#import "Kvo.h"
@interface ViewController : UIViewController <
UITextFieldDelegate
>
@property (nonatomic, strong) Kvo* aKvo;
@property (nonatomic, strong) UITextField* kvoTextField;
#import "ViewController.h"
#import "Kvo.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor systemBlueColor];
_aKvo = [[Kvo alloc] init];
_aKvo.kvoString = @"XUPT";
[_aKvo addObserver:self forKeyPath:@"kvoString" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
_kvoTextField = [[UITextField alloc] init];
_kvoTextField.backgroundColor = [UIColor whiteColor];
_kvoTextField.frame = CGRectMake(100, 100, 200, 50);
_kvoTextField.placeholder = _aKvo.kvoString;
_kvoTextField.delegate = self;
[self.view addSubview:_kvoTextField];
UIButton* startButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
startButton.frame = CGRectMake(100, 170, 100, 50);
[startButton setTitle:@"start" forState:UIControlStateNormal];
[startButton setTitleColor:[UIColor orangeColor] forState:UIControlStateNormal];
[startButton addTarget:self action:@selector(pressChange) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:startButton];
}
// 设置按钮触发监听
- (void)pressChange {
NSDictionary *aDictionary = [NSDictionary dictionaryWithObjectsAndKeys:_kvoTextField.text,@"kvoString", nil];
[_aKvo setValuesForKeysWithDictionary:aDictionary];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"kvoString"]) {
NSLog(@"old nameWord: %@", [change objectForKey:@"old"]);
NSLog(@"new nameWord: %@", [change objectForKey:@"new"]);
}
}
- (void)dealloc {
[_aKvo removeObserver:self forKeyPath:@"kvoString"];
}
@end
文章参考:iOS KVO 详解