Key Value Coding是cocoa的一个标准组成部分,它能让我们能够通过name(key)的方式訪问属性,某些情况下极大地简化了代码。可称之为cocoa的大招。
例如以下的样例:
使用KVC的优点
不使用KVC
- (id)tableView:(NSTableView *)tableview
objectValueForTableColumn:(id)column row:(NSInteger)row {
ChildObject *child = [childrenArray objectAtIndex:row];
if ([[column identifier] isEqualToString:@"name"]) {
return [child name];
}
if ([[column identifier] isEqualToString:@"age"]) {
return [child age];
}
if ([[column identifier] isEqualToString:@"favoriteColor"]) {
return [child favoriteColor];
}
// And so on.
}
使用KVC
- (id)tableView:(NSTableView *)tableview
objectValueForTableColumn:(id)column row:(NSInteger)row {
ChildObject *child = [childrenArray objectAtIndex:row];
return [child valueForKey:[column identifier]];
}
显而易见,简化了非常多代码。
KVC操作
KVC赋值
1 给当前对象属性赋值
- (void)setValue:(id)value forKey:(NSString *)key;
2给对象的属性的属性赋值
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
3 处理没有定义的键
- (void) setValue:(id)value forUndefinedKey:(NSString *)key
4 字典转模型:会为我们把和dictionary的key名字同样的class proerty设置上dict中key相应的value
- (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues;
注意:要求字典中的key和对象属性一样。都是主要的OC数据类型:Array/Dictionary/Boolean/Data/Number/String
KVC取值
1 获取对象属性的值
- (id)valueForKey:(NSString *)key;
2 获取对象属性的属性的值
- (id)valueForKeyPath:(NSString *)keyPath;
样例:
Person * p = [[Person alloc]init];
Car *car = [[Car alloc]init];
p.car = car;
[p setValue:@"qhyuan" forKeyPath:@"name"];
[p setValue:@(20) forKey:@"id"];
[p setValue:@"baoshijie" forKeyPath:@"car.brand"];
[p setValue:@"180000" forKeyPath:@"car.price"];
NSLog(@"kvc賦值的person对象----%@",p);
NSString * name = [p valueForKey:@"name"];
NSString * brand = [p valueForKeyPath:@"car.brand"];
NSLog(@"%@ %@",name, brand);
字典转模型
常规情况
模型
Person.h
@interface Person : NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) int age;
- (instancetype) initWithDict:(NSDictionary *) dict;
+ (instancetype) personWithDict:(NSDictionary *) dict;
+ (NSArray *) person;
@end
Person.m
@implementation Person
- (instancetype) initWithDict:(NSDictionary *) dict
{
if(self = [self init])
{
// 使用KVC 字典转模型 如此方便。省去了大量的赋值代码
[self setValuesForKeysWithDictionary:dict];
//self.name = dict[@"name"];
//self.age = [dict[@"age"] integerValue];
}
return self;
}
+ (instancetype) personWithDict:(NSDictionary *) dict
{
return [[self alloc]initWithDict:dict];
}
+ (NSArray *) person
{
NSMutableArray * mutablePersons = [NSMutableArray array];
NSString * path = [[NSBundle mainBundle] pathForResource:@"persons.plist" ofType:nil];
NSArray *persons = [[NSArray alloc] initWithContentsOfFile:path];
for (NSDictionary * person in persons) {
[mutablePersons addObject:[self personWithDict:person]];
}
return mutablePersons;
}
- (NSString *) description
{
NSString * desc = [NSString stringWithFormat:@"<%p:(%@,%d)>",self,self.name,self.age];
return desc;
}
@end
字典中多个某些key是OC中的keyword
假设将键age换成了id
会抛出异常:*** Terminating app due to uncaught exception 'NSUnknownKeyException',reason: '[<Person 0x8c419a0> setValue:forUndefinedKey:]: this class isnot key value coding-compliant for the key id.
重写下面方法就可以,处理没有定义的键
- (void)setValue:(id)value forUndefinedKey:(NSString *)key;
解决方案:
- (void) setValue:(id)value forUndefinedKey:(NSString *)key
{
if([key isEqualToString:@"id"])
key = @"age";
[super setValue:value forKey:key];
}
字典里面还包括某些相应自己定义类的字典或者数组
Person类添加了一个Car类型的属性
@property (nonatomic, strong) Car * car;
我们仅仅须要重写下面方法
- (void)setValue:(id)value forKey:(NSString *)key;
解决方法:
- (void)setValue:(id)value forKey:(NSString *)key
{
if([key isEqualToString:@"cars"])
{
Car *car = [Car carWithDict:(NSDictionary *)value];
self.car = car;
}
else
[super setValue:value forKey:key];
}
打印结果
字典转模型[5525:60b] (
"<Person:(zhangsan,20,<Car:(benchi,180000)>)>",
"<Person:(lisi,22,<Car:(baoma,180000)>)>",
"<Person:(wangwu,24,<Car:(aodi,180000)>)>"
)
假设不仅仅是添加了Cars属性而是添加了Cars数组,也是类似的方式。