KVO简介
KVO全称KeyValue Observing,是苹果提供的一套事件通知机制。
作用:允许对象监听另一个对象特定属性的改变,并在改变时接收到事件。
注意:KVO只对属性发生作用,一般继承自NSObject的对象都默认支持KVO。
使用步骤
-
注册观察者
使用方法:addObserver:forKeyPath:options:context:
参数含义:
1.observer:观察者,监听属性变化的对象。 该对象必须实现 observeValueForKeyPath:ofObject:change:context: 方法。
2. keyPath:要观察的属性名称。要和属性声明的名称一致。
3. options:对KVO机制进行配置,修改KVO通知的时机以及通知的内容
4. context: 传入任意类型的对象,在"接收消息回调"的代码中可以接收到这个对象,是KVO中的一种传值方式。
options参数:
enum {
NSKeyValueObservingOptionNew = 0x01,
NSKeyValueObservingOptionOld = 0x02,
NSKeyValueObservingOptionInitial = 0x04,
NSKeyValueObservingOptionPrior = 0x08
};
typedef NSUInteger NSKeyValueObservingOptions;
默认只接受新值
NSKeyValueObservingOptionNew:接收方法中使用change参数传入变化后的新值,键为:NSKeyValueChangeNewKey;
NSKeyValueObservingOptionOld:接收方法中使用change参数传入变化前的旧值,键为:NSKeyValueChangeOldKey;
NSKeyValueObservingOptionInitial:注册之后立刻调用接收方法,如果配置了NSKeyValueObservingOptionNew,change参数内容会包含新值,键为:NSKeyValueChangeNewKey;
NSKeyValueObservingOptionPrior:如果加入这个参数,接收方法会在变化前后分别调用一次,共两次,变化前的通知change参数包含notificationIsPrior = 1。 -
接收通知
使用方法:observeValueForKeyPath:ofObject:change:context: -
取消注册
使用方法:removeObserver:forKeyPath:context
例子
首先创建一个继承自NSObject的massage类,用于存放账号和密码
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface massage : NSObject
@property (nonatomic, strong) NSString *strNameWord;
@property (nonatomic, strong) NSString *strPassWord;
@end
然后在ViewController.m里创建两个TextField和一个用于确认修改账号的button
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_aMassage = [[massage alloc] init];
_aMassage.strNameWord = @"123";
_aMassage.strPassWord = @"456";
//给账号和密码注册监听
[_aMassage addObserver:self forKeyPath:@"strNameWord" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
[_aMassage addObserver:self forKeyPath:@"strPassWord" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
_nameTextField = [[UITextField alloc] init];
[self.view addSubview:_nameTextField];
_nameTextField.frame = CGRectMake(50, 150, 300, 50);
_nameTextField.layer.masksToBounds = YES;
_nameTextField.layer.borderWidth = 2;
_nameTextField.layer.cornerRadius = 5;
_nameTextField.layer.borderColor = [UIColor blackColor].CGColor;
_nameTextField.delegate = self;
_passTextField = [[UITextField alloc] init];
[self.view addSubview:_passTextField];
_passTextField.frame = CGRectMake(50, 230, 300, 50);
_passTextField.layer.masksToBounds = YES;
_passTextField.layer.borderWidth = 2;
_passTextField.layer.cornerRadius = 5;
_passTextField.layer.borderColor = [UIColor blackColor].CGColor;
_passTextField.delegate = self;
UIButton *reviseBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[self.view addSubview:reviseBtn];
[reviseBtn addTarget:self action:@selector(pressBtn) forControlEvents:UIControlEventTouchUpInside];
[reviseBtn setTitle:@"revise" forState:UIControlStateNormal];
[reviseBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
reviseBtn.frame = CGRectMake(150, 320, 100, 50);
}
在button点击事件里触发监听
- (void)pressBtn {
NSLog(@"revise!");
//触发监听
//第一种方法
NSDictionary *newMassageDictionary = [NSDictionary dictionaryWithObjectsAndKeys:_nameTextField.text,@"strNameWord",_passTextField.text,@"strPassWord", nil];
[_aMassage setValuesForKeysWithDictionary:newMassageDictionary];
//第二种方法
// [_aMassage setValue:@"000" forKey:@"strPassWord"];
}
当KVO事件到来时会调用这个方法,如果没有实现会导致Crash
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"strNameWord"]) {
NSLog(@"old nameWord: %@", [change objectForKey:@"old"]);
NSLog(@"new nameWord: %@", [change objectForKey:@"new"]);
}
if ([keyPath isEqualToString:@"strPassWord"]) {
NSLog(@"old PassWord: %@", [change objectForKey:@"old"]);
NSLog(@"new PassWord: %@", [change objectForKey:@"new"]);
}
}
最后移除监听
- (void)dealloc {
[_aMassage removeObserver:self forKeyPath:@"strPassWord"];
}
效果图:
再次修改账号密码