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"];
执行的机制如下:
- 优先考虑调用属性的
set方法
,即setName - 若是该类没有对应的set方法,KVC会搜索该类名为
_name
,无论_name是在类接口部分定义,还是在类实现部分定义也无论用哪个访问控制符修饰,然后对_name成员变量赋值 - 如果该类既没有setName:方法,也没有_name成员变量,KVC会搜索名为
name
的成员变量,然后对name成员变量赋值 - 若是以上3条都没有找到,会执行对象的
setValue:forUndefineKey
方法,默认这个方法会引起异常导致程序结束
对于[user valueForKey:@"name"]
,执行的机制如下:
- 优先考虑调用name的
get方法
- 若该类没有name的get方法,KVC机制会搜索该类
_name
的成员变量 - 若还是找不到,则搜索name的成员变量
- 最后若还是找不到,执行
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类型属性