iOS KVC、KVO、通知
一、KVC
KVC即是指 NSKeyValueCoding,一个非正式的 Protocol,我们都知道OC语言是面对对象的,而KVC提供一种机制来间接访问对象的属性。下面给大家举个例子
这里有一个Person类,这个类有一个name属性,用property系统会自动实现其的setter和getter方法,可直接用点语法调用,否则需要手动实现setter和getter方法
Person 的 .h方法
@interface Person : NSObject
{
NSString * _name;
}
//name的setter和getter方法
- (NSString *)name;
- (void)setName:(NSString *) name;
//用property系统会自动实现age的setter和getter方法,可直接用点语法调用
@property (nonatomic,assign) NSInteger age;
@end
Person 的.m方法
@implementation Person
-(NSString *)name {
return _name;
}
- (void)setName:(NSString *)name {
_name = name;
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
Person * person1 = [[Person alloc] init];
[person1 setValue:@"小明" forKey:@"name"];
NSString * person1Name = [person1 valueForKey:@"name"];
NSLog(@"person1Name = %@",person1Name);
[person1 setValue:@"160" forKey:@"age"];
NSString * person1Height = [person1 valueForKey:@"age"];
NSLog(@"person1Height = %@",person1Height);
Person * person2 = [[Person alloc] init];
person2.name = @"小红";
person2.age = 175.0;
NSString * person2Name = person2.name;
NSLog(@"person2Name = %@",person2Name);
CGFloat person2Height = person2.age;
NSLog(@"person2Height = %lf",person2Height);
}
看看下面的控制台,相信你会理解KVC
二、KVO
在 Cocoa Touch
框架中通知和 KVO
都实现了观察者模式。通知是由一个中心对象为所有观察者提供变更通知,KVO
是被观察的对象直接向观察者发送通知。
Key-Value Observing (KVO) 建立在 KVC 之上,它能够观察一个对象的 KVC key path 值的变化。
KVO是基于runtime
机制实现的,当某个类的属性对象第一次被观察时,系统就会在运行期间动态地创建该类的一个派生类,在这个派生类中重写基类的任何被观察属性的setter
方法。派生类在被重写的setter方法内实现真正的通知机制。
KVO提供了一种机制,当指定的被观察的对象的属性被修改后,KVO会自动通知响应的观察者。
直接上代码:这里以两个label为例,点击btn按钮,改变label1的text,通过KVO监听模式,使label2的text和label1一样。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic,strong) UILabel * label1;
@property (nonatomic,strong) UIButton * button;
@property (nonatomic,strong) UILabel * label2;
@end
- (void)viewDidLoad {
[super viewDidLoad];
[self createUI];
//(注册监听对象。anObserver指监听者,keyPath就是要监听的属性值,而context方便传输你需要的数据,它是个指针类型。
[self.label1 addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:NULL];
}
- (void) createUI {
[self.view addSubview:self.label1];
[self.view addSubview:self.label2];
[self.view addSubview:self.button];
}
- (UILabel *)label1 {
if (!_label1) {
_label1 = [[UILabel alloc] initWithFrame:CGRectMake(50, 30, 200, 30)];
_label1.text = @"我是测试label";
_label1.backgroundColor = [UIColor cyanColor];
}
return _label1;
}
- (UILabel *)label2 {
if (!_label2) {
_label2 = [[UILabel alloc] initWithFrame:CGRectMake(50, 80, 200, 30)];
_label2.text = @"我是测试label";
_label2.backgroundColor = [UIColor cyanColor];
}
return _label2;
}
- (UIButton *)button {
if (!_button) {
_button = [[UIButton alloc] initWithFrame:CGRectMake(100, 120, 60, 30)];
_button.backgroundColor = [UIColor redColor];
[_button addTarget:self action:@selector(clickTheBtn) forControlEvents:UIControlEventTouchUpInside];
}
return _button;
}
- (void) clickTheBtn {
NSInteger i = random() % 5 + 1;
_label1.text = [NSString stringWithFormat:@"%ld",i];
}
//这里为了防止内存泄漏要移除观察者label1
- (void)dealloc {
[_label1
removeObserver:self forKeyPath:@"text"];
}
//实现监听方法。监听方法在Value(属性的值)发生变化的时候自动调用。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
//其中,object指被监听的对象。change里存储了一些变化的数据,比如变化前的数据,变化后的数据。
if ([object isEqual:self.label1] && [keyPath isEqualToString:@"text"]) {
_label2.text = _label1.text;
}
}
效果如下:
三、通知
通知:是一种广播机制,在实践发生的时候,通过通知中心对象,一个对象能够为所有关心这个时间发生的对象发送消息,两者都是观察者模式,不同于在KVO是被观察者直接发送消息给观察者,是对象间的直接交互,通知则是两者都和通知中心对象交互,对象之间不知道彼此。
这里仍然以上述两个label为例,通过改变label1的text,使label2的text和label1的text一样
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic,strong) UILabel * label1;
@property (nonatomic,strong) UIButton * button;
@property (nonatomic,strong) UILabel * label2;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self createUI];
// Observer:监听者
// selector:监听者接到通知后要执行的方法
// name:通知的名称
// object:通知的发布者
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doSomeSthing) name:@"lableText" object:_label1];
}
- (void) createUI {
[self.view addSubview:self.label1];
[self.view addSubview:self.label2];
[self.view addSubview:self.button];
}
- (UILabel *)label1 {
if (!_label1) {
_label1 = [[UILabel alloc] initWithFrame:CGRectMake(50, 30, 200, 30)];
_label1.text = @"我是测试label";
_label1.backgroundColor = [UIColor cyanColor];
}
return _label1;
}
- (UILabel *)label2 {
if (!_label2) {
_label2 = [[UILabel alloc] initWithFrame:CGRectMake(50, 80, 200, 30)];
_label2.text = @"我是测试label";
_label2.backgroundColor = [UIColor cyanColor];
}
return _label2;
}
- (UIButton *)button {
if (!_button) {
_button = [[UIButton alloc] initWithFrame:CGRectMake(100, 120, 60, 30)];
_button.backgroundColor = [UIColor redColor];
[_button addTarget:self action:@selector(clickTheBtn) forControlEvents:UIControlEventTouchUpInside];
}
return _button;
}
- (void) clickTheBtn {
NSInteger i = random() % 5 + 1;
_label1.text = [NSString stringWithFormat:@"%ld",i];
// notificationWithName:通知名称
// object:发布者
// userInfo:发布的信息
[[NSNotificationCenter defaultCenter] postNotificationName:@"lableText" object:_label1 userInfo:nil];
}
- (void) doSomeSthing {
_label2.text = _label1.text;
}
//移除通知的观察者
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
效果如下: