本文共 5714 字,大约阅读时间需要 19 分钟。
一、KVC
KVC即是指 NSKeyValueCoding(键值编码),KVC提供了一种间接访问属性方法或成员变量的机制,可以通过字符串来访问对象的的属性方法或成员变量。不是直接调用getter 和 setter方法。通常我们使用valueForKey 来替代getter 方法,setValue:forKey来代替setter方法。这样就可以在运行时动态在访问和修改对象的属性,而不是在编译时确定。
KVC底层实现过程:
当一个对象调用setValue方法时,方法内部会做出以下操作:
1.检查相应key值是否存在setter方法,如果存在调用setter方法
2.如果setter方法不存在,就在查找与key相同的名称并且带下划线的成员变量,如果有则直接给成员属性赋值。
3.如果没有找到_key 就会查找相同属性名称的key,如果有就直接赋值。
4.如果还没有找到则调用valueForUndefiedKey:和setValue:forUndefinedkey方法方法
这些方法的的实现都是抛出异常,我们根据需求去重写他们。
KVC 的使用场景:1.动态的取值和设值 2.model和字典的转化 3.用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; @endPerson 的.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
是被观察的对象直接向观察者发送通知。
KVO是基于runtime
机制实现的,当某个A类的属性对象第一次被观察时,系统就会在运行期间动态地创建该类的一个派生类B,B继承A,将A类的isa指针指向B类,在这个派生类B中重写被观察的属性的setter方法 ,重写的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]; }效果如下: