KVC、KVO的用法
一、KVC
1.概念
KVC是Key Value Coding的简称,它是一种可以直接通过字符串的名字(Key)来访问类属性的机制。使用该机制不需要调用 存取方法和变量实例就可以访问对象属性(即使属性用@private修饰仍然可以访问,很黄很暴力,破坏封装性)。本质上讲,键- 值编码定义了你的程序存取方法要实现的样式及方法签名。
在应用程序中实现键-值编码兼容性是一项重要的设计原则。存取方法可以加强合适的数据封装,而键-值编码方法在多数情 况下可简化程序代码。
键-值 编码方法在Objective-C非标准协议(类目)NSKeyValueCoding 中被声明,默认的实现方法由NSObject提供。
键值编码支持带有对象值的属性,同时也支持纯数值类型和结构。非对象参数和返回类型会被识别并自动封装/解封。
2.KVC的基本用法
设置和访问:
键、值编码中的基本调用包括 - valueForKey: 和 - setValue: forKey: 这两个方法,他们以字符串的形式向对象发送消 息,字符串是我们关注属性的关键。
看代码中具体怎么使用:现在我们有个Person类,Book类,
<span style="font-family:SimHei;font-size:14px;">//Person.h
#import <Foundation/Foundation.h>
@class Book;
@interface Person : NSObject{
@private
NSString *_name;
}
//@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) Book *book;
- (instancetype)initWithName:(NSString *)name;
@end
</span>
<span style="font-family:SimHei;font-size:14px;">#import "Person.h"
#import "Book.h"
@implementation Person
- (instancetype)initWithName:(NSString *)name{
if (self = [super init]) {
_name = name;
}
return self;
}
</span>
在ViewController中写如下代码:
<span style="font-family:SimHei;font-size:14px;">- (void)viewDidLoad {
[super viewDidLoad];
Person *p1 = [[Person alloc] initWithName:@"Jack"];
Person *p2 = [[Person alloc] init];
NSString *name0 = [p1 valueForKey:@"name"];
NSString *name1 = [p1 valueForKeyPath:@"name"];
[p2 setValue:@22 forKey:@"age"];
NSInteger age = [[p2 valueForKey:@"age"] integerValue];
NSLog(@"name0 : %@ name1 : %@", name0, name1);
NSLog(@"p2's age : %ld", age);
}
</span>
运行结果
<span style="font-family:SimHei;font-size:14px;">2015-05-02 20:36:34.803 review[1873:122845] name0 : Jack name1 : Jack
2015-05-02 20:36:34.804 review[1873:122845] p2's age : 22
</span>
可以看到,通过-setValue: ForKey: 为p2 的age 设了值,通过 valueForKey: 或者 valueForKeyPath 能够访问到Person的属性name的值。
KVC在设置或访问属性的值时,先看是否有setter getter方法,如果不存在,将查找内部是否有_name或者name实例变量。
ForKey: 与 ForKeyPath:区别
ForkeyPath 可以为支持指定路径,用『点』号隔开
在viewDidLoad中添加如下代码:
<span style="font-family:SimHei;font-size:14px;">Book *book1 = [[Book alloc] init];
p1.book = book1;
[p1 setValue:@"葵花宝典" forKeyPath:@"book.name"];
NSString *bookName = [p1 valueForKeyPath:@"book.name"];
NSLog(@"bookName : %@", bookName);
</span>
运行结果:
<span style="font-family:SimHei;font-size:14px;">2015-05-02 21:07:47.031 review[2068:132141] name0 : Jack name1 : Jack
2015-05-02 21:07:47.033 review[2068:132141] p2's age : 22
2015-05-02 21:07:47.034 review[2068:132141] bookName : 葵花宝典
</span>
通过 【点】号 .属性 可以指定路径 访问任意的属性
KVC的其他用法:
<span style="font-family:SimHei;font-size:14px;"> book1.price = 34.9;
Book *book2 = [[Book alloc] init];
book2.name = @"jiuyinzhenjing";
book1.price = 56.5;
Book *book3 = [[Book alloc] init];
book3.name = @"rulaishenzhang";
book3.price = 79.0;
p1.books = @[book1, book2, book3];
NSString *names = [p1 valueForKeyPath:@"books.name"];
NSLog(@" names : %@", names);
float sumPrice = [[p1 valueForKeyPath:@"books.@sum.price"] floatValue];
NSLog(@"sumPrice : %f", sumPrice);</span>
运行结果:
<span style="font-family:SimHei;font-size:14px;">2015-05-02 21:25:49.090 review[2168:137783] name0 : Jack name1 : Jack 2015-05-02 21:25:49.092 review[2168:137783] p2's age : 22 2015-05-02 21:25:49.092 review[2168:137783] bookName : kuihuabaodian 2015-05-02 21:25:49.092 review[2168:137783] names : ( kuihuabaodian, jiuyinzhenjing, rulaishenzhang ) 2015-05-02 21:25:49.093 review[2168:137783] sumPrice : 135.500000 </span>
二、KVO
KVO( Key Value Observing), 直译为:基于键值观察者。它提供一种机制,当指定的对象的属性被修改后,则对象就会接 受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。
你可以观察任意对象属性,包括简单属性,对一或者对多关系。对多关系的观察者将会被告知发生变化的类型-也就是任意,发生 变化的对象。
KVO的基本用法:
注册观察者、接收变更通知、移除观察者。
1.注册观察者:
[p1 addObserver:book1 forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
[p1 setValue:@"QQ" forKey:@"name"];
2.当被观察的属性的值改变时,就会调用观察者的如下方法:(写在Book.m中)
<span style="font-family:SimHei;font-size:14px;">- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
NSLog(@" %@ %@ %@",object, keyPath, change);
}
</span>
3.移除观察者。
<span style="font-family:SimHei;font-size:14px;">[p1 removeObserver:book1 forKeyPath:@"name"];
</span>
KVO的底层实现机制:
KVO底层是由runtime 机制实现的。
当被观察者的属性值发生变化时,runtime 会自动为被观察者生产一个子类,KVONotifying父类名,生成的子类中有setter方法,调用这个setter方法,这个setter方法中还会调用两个方法,在这个两个方法之间调用了
<span style="font-family:SimHei;font-size:14px;">- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context</span>
方法;