KVO键值监听机制

KVO:key-value-observing,键值监听。

KVO的使用被叫做观察者设计模式,这种设计模式提供了一种机制:指定一个对象的属性,当被观察的对象属性发生变化的时候,就会发送通知作出也相应的处理。

这里需要注意一点:被观察的对象属性必须实现了setter、getter方法或者使用KVC存取。

KVO在实际开发中的使用:

1、监听某个对象属性,当属性发生变化时,实现一些操作。

2、实现model和view之间的通信。


KVO色基本原理:

当观察一个对象A时,KVO机制动态创建一个A对象当前类的子类,并为这个新的子类重写被观察属性的setter方法,setter方法随后负责通知观察对象属性值得改变状况。



扫描二维码关注公众号,回复: 3213022 查看本文章

KVO的使用:KVO的使用比较简单,只有3步

1、给被观察的对象属性添加观察者,调用方法:

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullablevoid *)context;

2、实现回调方法,在回调方法中做需要的操作,实现回调方法:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context;

3、移除观察者,调用方法:

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

下面,写一个简单的示例:

有一个Studen类,类中有一个name属性,监听name属性,当name发生变化的时候,打印一下。

示例完整代码如下:

Student类代码:

#import <Foundation/Foundation.h>


@interface Student : NSObject


@property (nonatomic,copy)NSString *name;


@end

ViewController中代码:

#import "ViewController.h"

#import "Student.h"


@interface ViewController ()

{

    Student *student;

}

@end


@implementation ViewController


- (void)viewDidLoad {

    [superviewDidLoad];

    

    //给对象属性添加观察者方法

    [selfuseKVOFaction];

    

    //创建视图方法

    [selfsetUI];

}


- (void)setUI{

    UILabel *label = [[UILabelalloc]initWithFrame:CGRectMake(100, 300, 200, 30)];

    label.backgroundColor = [UIColoryellowColor];

    label.tag = 1000;

    label.text =student.name;

    [self.viewaddSubview:label];

    

    UIButton *button = [UIButtonbuttonWithType:UIButtonTypeCustom];

    button.backgroundColor = [UIColorblackColor];

    button.frame =CGRectMake(100, 200, 200, 30);

    [button setTitle:@"修改name的值"forState:UIControlStateNormal];

    [button addTarget:self action:@selector(changeValue:)forControlEvents:UIControlEventTouchUpInside];

    [self.viewaddSubview:button];

}


- (void)useKVOFaction{

    //创建一个Student对象

    student = [[Studentalloc]init];

    //给对象属性赋值

    student.name =@"名字";

    //student对象的name属性添加观察者

    [studentaddObserver:selfforKeyPath:@"name"options:NSKeyValueObservingOptionNewcontext:nil];

}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{

    //student对象的name属性发生变化的时候会在视图上显示新的名字

    UILabel *label = [self.viewviewWithTag:1000];

    label.text =student.name;

}


- (void)changeValue:(UIButton *)btn{

    //使用KVCstudent对象的name属性赋值

    [studentsetValue:@"新名字"forKey:@"name"];

    //操作完成后,移除监听

    [studentremoveObserver:selfforKeyPath:@"name"];

}


@end

代码运行结果:

点击按钮前:                                                               点击按钮后:

                             

在上面的示例中

//student对象的name属性添加观察者

[student addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

给student的name属性添加方法,student是被观察的对象,name是被观察的对象属性。

addObserver: 后跟的参数书观察者,也就是说是谁要观察监听student这个对象的name属性,这里的self表示当前的控制器是观察者。

forKeyPath:@"name"后面的参数就是对象的属性,这里的字符串name。

options:后面的参数:

typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {

    NSKeyValueObservingOptionNew = 0x01,

    NSKeyValueObservingOptionOld = 0x02,

    NSKeyValueObservingOptionInitial NS_ENUM_AVAILABLE(10_5, 2_0) = 0x04,

    NSKeyValueObservingOptionPrior NS_ENUM_AVAILABLE(10_5, 2_0) = 0x08

};

NSKeyValueObservingOptionNew:提供更改前的值

NSKeyValueObservingOptionOld:提供更改后的值

NSKeyValueObservingOptionInitial:观察最初的值,在注册观察者的时候会触发一次回调方法

NSKeyValueObservingOptionPrior:分别在值修改前后出触发方法,一次修改有两次回调方法的调用。

context:后跟的参数是要传递的东西,可以是任何类型,在使用的时候注意类型转化,有了这个参数的传入就可以实现一些通信,从而可以实现一些类之间的解耦。



- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{

    //student对象的name属性发生变化的时候会在视图上显示新的名字

    UILabel *label = [self.view viewWithTag:1000];

    label.text = student.name;

}

这个回调方法,是要处理的操作,在被监听的对象属性发生变化的时候,这个回调方法就被调用,触发调用的次数根据options的参数决定。


上面的代码中,是在按钮的点击事件里移除的监听。而且是在修改了name的值以后。

- (void)changeValue:(UIButton *)btn{

    //使用KVCstudent对象的name属性赋值

    [student setValue:@"新名字" forKey:@"name"];

    //操作完成后,移除监听

    [student removeObserver:self forKeyPath:@"name"];

}

观察者的移除一定要注意,要在合适的地方移除监听。当监听被移除以后,这个监听就没有了,所以当再次点击按钮的时候

[student removeObserver:self forKeyPath:@"name"];

这段代码还是会被执行,但是已经没有监听了,这样就会造成系统崩溃。所以,在上面的代码中,在按钮的点击事件中移除监听就不是一个合适的地方。那么什么才是合适的地方呢,在开发中,最长用到的地方时在ViewController的声明周期的回调方法中去移除,而且是在视图将要消失或者视图已经消失的方法中去移除监听。将上面代码做简单修改:

[student removeObserver:self forKeyPath:@"name"];

方法,放在ViewController的回调方法viewDidDisappear中

- (void)viewDidDisappear:(BOOL)animated{

    [selfviewDidDisappear:YES];

    //操作完成后,移除监听

    [studentremoveObserver:selfforKeyPath:@"name"];

}

这样就不会有系统崩溃的问题了。


KVO作为一种常用的设计模式,很简单,只有一个简单的回调就可以实现想要的操作,而且提供了观察新值、旧值和初始值的参数。但是由于KVO的实现原理的原因,创建子类,重写setter方法等都是相对消耗内存的,所以,在使用的时候还是要根据实际情况合理使用。

猜你喜欢

转载自blog.csdn.net/m0_37681833/article/details/67633722
KVO
今日推荐