一、KVC:
1、概述
KVC,即NSKeyValueCoding,它提供了一种在运行时而非编译时动态访问对象属性及成员变量的方法,允许我们用属性的字符串名称(该字符串名称叫“键(key)”)来访问属性。这里,key只是一个字符串,它对应的值可以是任意对象。这一特性有些类似于Java中的反射。例如:
// 1、访问对象的属性
@property (copy, nonatomic) NSString *strName;
// 取值
NSString *str = [object valueForKey@"strName"];
// 设定
[object setValue:@"9527" forKey@"strName"];
// 2、访问标量或struct
@property (nonatomic) CGFloat height;
[object setValue:@(20) forKey:@"height"]
2、使用方法
KVC有两个最基础的方法:设置key的值、获取key的值。
①、setValue:forKey:
setValue:forKeyPath:
setValue:forUndefinedKey:
setNilValueForKey:
②、valueForKey:
valueForKeyPath:
valueForUndefinedKey:
mutableArrayValueForKey:有序一对多关系成员
mutableSetValueForKey:无序一对多关系成员
使用时需要注意以下几点:
- 这里的valueForKey:方法用于以字符串调用对象的get属性方法,或者读取成员变量的值;与之相对的是setValue:forKey:,它用于以字符串调用对象的set属性方法,或者修改成员变量的值。
- 对于基本数据类型,KVC方法会对基本数据类型进行封装(基本数据类型封装为NSNumber,其他结构体类型封装为NSValue),如:valueForKey:方法返回的是NSNumber对象,需要再调用intValue取出其中的值。setValue:forKey:方法与之类似,接收NSNumber参数。
- 在使用KVC时,如果找不到字符串对应的属性和成员变量时会调用valueForUndefinedKey:或者setValue:forUndefinedKey:这两个方法,默认情况下会抛出异常。
- 默认情况下KVC方法能够直接访问类的私有成员变量,如果我们不想这样,可以重写accessInstanceVariablesDirectly方法,并令其返回NO(默认是返回YES)。KVC方法定义在NSKeyValueCoding类别中,该类别附加于NSObject类上,所以所有对象都具有这些方法。
- 在一些特殊的类的对象上调用KVC方法会有特别的效果。对于数组NSArray、集合NSSet,调用valueForKey:会对每个数组和集合成员调用valueForKey:,并返回新的数组或者集合。
3、示例
一个Person对象有一个name和一个address属性,那么,按照KVC的说法,Person对象有如下两个键值对:@{@”name", name},@{@"address", address}。
void changeName(Person *p, NSString *newName)
{
// using the KVC accessor (getter) method
NSString *originalName = [p valueForKey:@"name"];
// using the KVC accessor (setter) method.
[p setValue:newName forKey:@"name"];
NSLog(@"Changed %@'s name to: %@", originalName, newName);
}
如果Person对象有另外一个key 配偶(spouse),而spouse也是一个Person对象,那么可以用@“spouse.name" key访问该spouse对象的name的值(注意,用的是valueForKeyPath:):
void logMarriage(Person *p)
{
// just using the accessor again, same as example above
NSString *personsName = [p valueForKey:@"name"];
// this line is different, because it is using
// a "key path" instead of a normal "key"
NSString *spousesName = [p valueForKeyPath:@"spouse.name"];
NSLog(@"%@ is happily married to %@", personsName, spousesName);
}
二、KVO
1、概述
KVO(Key-Value Observing)建立在KVC基础之上,它能够观察指定的对象的KVC key path值的变化,即每次当被观察对象的属性值发生改变时,KVO会自动通知相应的观察者。这一机制类似于设计模式中的观察者模式。
2、使用方法
以上述的Person对象为例:
①、注册,指定被观察对象的属性:watchPersonForChangeOfAddress
②、实现回调方法,在被观察对象的key path值改变时调用:observeValueForKeyPath:ofObject:change:context
③、移除观察:removeObservers
3、示例
static NSString *const KVO_CONTEXT_ADDRESS_CHANGED = @"KVO_CONTEXT_ADDRESS_CHANGED";
@implementation PersonWatcher
-(id) init;
{
if(self = [super init]){
m_observedPeople = [NSMutableArray new];
}
return self;
}
// 指定被观察对象的属性
-(void)watchPersonForChangeOfAddress:(Person *p)
{
// this begins the observing
[p addObserver:self
forKeyPath:@"address"
options:0
context:KVO_CONTEXT_ADDRESS_CHANGED];
// keep a record of all the people being observed,
// because we need to stop observing them in dealloc
[m_observedPeople addObject:p];
}
// 回调方法
// whenever an observed key path changes, this method will be called
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
// use the context to make sure this is a change in the address,
// because we may also be observing other things
if(context == KVO_CONTEXT_ADDRESS_CHANGED) {
NSString *name = [object valueForKey:@"name"];
NSString *address = [object valueForKey:@"address"];
NSLog(@"%@ has a new address: %@", name, address);
}
}
// 移除观察
-(void)dealloc
{
// must stop observing everything before this object is
// deallocated, otherwise it will cause crashes
for(Person *p in m_observedPeople){
[p removeObserver:self forKeyPath:@"address"];
}
[m_observedPeople release];
m_observedPeople = nil;
}
@end