KVC介绍


1. Key Value Coding(键值编码)

最基本的KVC由NSKeyValueCoding协议支持,最基本的操作由以下几个完成:

  • -(id)valueForKey:(NSString *)key;
  • -(void)setValue:(id)value forKey:(NSString *)key;
  • -(id)valueForKeyPath:(NSString *)keyPath;
  • -(void)setValue:(id)value forKeyPath:(NSString *)keyPath;

1.1 简单使用KVC,KVC的执行机制

例如一个类:

#import <Foundation/Foundation.h>

@interface FKUser : NSObject
// 使用@property定义3个property
@property (nonatomic, copy) NSString* name;
@property (nonatomic, copy) NSString* pass;
@property (nonatomic, copy) NSDate* birth;
@end

使用kvc:

#import "FKUser.h"

int main(int argc , char * argv[])
{
@autoreleasepool{
	// 创建FKUser对象
	FKUser* user = [[FKUser alloc] init];
	// 使用KVC方式为name属性设置属性值
	[user setValue:@"孙悟空" forKey:@"name"];
	// 使用KVC方式为pass属性设置属性值	
	[user setValue:@"1455" forKey:@"pass"];
	// 使用KVC方式为birth属性设置属性值
	[user setValue:[[NSDate alloc]init] forKey:@"birth"];
	// 使用KVC获取FKUser对象的属性
	NSLog(@"user的name为:%@" , [user valueForKey:@"name"]);
	NSLog(@"user的pass为:%@" , [user valueForKey:@"pass"]);
	NSLog(@"user的birth为:%@" , [user valueForKey:@"birth"]);
}
}	

对于 [user setValue:@"孙悟空" forKey:@"name"];执行的机制如下:

  1. 优先考虑调用属性的set方法,即setName
  2. 若是该类没有对应的set方法,KVC会搜索该类名为_name,无论_name是在类接口部分定义,还是在类实现部分定义也无论用哪个访问控制符修饰,然后对_name成员变量赋值
  3. 如果该类既没有setName:方法,也没有_name成员变量,KVC会搜索名为name的成员变量,然后对name成员变量赋值
  4. 若是以上3条都没有找到,会执行对象的setValue:forUndefineKey方法,默认这个方法会引起异常导致程序结束

对于[user valueForKey:@"name"],执行的机制如下:

  1. 优先考虑调用name的get方法
  2. 若该类没有name的get方法,KVC机制会搜索该类_name的成员变量
  3. 若还是找不到,则搜索name的成员变量
  4. 最后若还是找不到,执行valueforUndefinedKey方法

1.2 处理不存在的key

重写一下两个方法就可以了。

- (void) setValue:(id)value forUndefinedKey:(id)key
{
NSLog(@"您尝试设置的key:【%@】并不存在!", key);
NSLog(@"您尝试设置的value为:%@", value);
}
- (void) valueForUndefinedKey:(id)key
{
NSLog(@"您尝试访问的key:【%@】并不存在!", key);
}

1.3 处理nil

当对基本类型数据使用kvc赋值为nil时也会出错,程序员抛出NSInvalidArgumentException

这时候需要重写如下方法:

- (void)setNilValueForKey: (id)key
{
// 如果尝试将key为price的属性设为nil
if([key isEqualToString:@"price"])
{
	// 将该price设置为0
	price = 0;
}
else
{
	// 回调父类的setNilValueForKey,执行默认行为
	[super setNilValueForKey: key];
}
}	

1.4 简单使用key路径

直接给代码吧:

FKItem.h文件:

#import <Foundation/Foundation.h>

@interface FKItem : NSObject
// 使用@property定义两个属性
@property(nonatomic , copy) NSString* name;
@property(nonatomic , assign) int price;
@end	

FKOrder.h文件:

#import <Foundation/Foundation.h>
#import "FKItem.h"

@interface FKOrder : NSObject
// 使用@property定义两个属性
@property(nonatomic , strong) FKItem* item;
@property(nonatomic , assign) int amount;
-(int) totalPrice;
@end	

使用:

int main(int argc , char * argv[])
{
@autoreleasepool{
	// 创建FKItem对象
	FKOrder* order = [[FKOrder alloc] init];
	// 使用KVC方式为amount设置属性值
	[order setValue:@"12" forKey:@"amount"];
	[order setValue:[[FKItem alloc] init] forKey:@"item"];
	// 使用setValue:forKeyPath设置item属性的name属性
	[order setValue:@"鼠标" forKeyPath:@"item.name"];
	// 使用valueForKeyPath来获取复合属性值
	[order setValue:[NSNumber numberWithInt:20] forKeyPath:@"item.price"];
	NSLog(@"订单包含%@个%@,总价为:%@", [order valueForKey:@"amount"] , 
		[order valueForKeyPath:@"item.name"],
		[order valueForKey:@"totalPrice"]);
}
}	

1.5 使用key路径聚合信息

使用 @count 、@sum、@avg、@min、@max、@distinctUnionOfObject获取对象数组中的聚合信息

一般形式为:

[keypath].[@operator].[property name]

例如

id sum = [workProject01 valueForKeyPath:@"listOfTasks.@sum.priority"];

listOfTasks 是 Task对象的数组,每个Task对象都有名为priority的int类型属性

posted @ 2016-04-19 15:11  孙焱焱  阅读(297)  评论(0编辑  收藏  举报