Key Value Coding是cocoa的一个标准组成部分,它能让我们可以通过name(key)的方式访问属性,某些情况下极大地简化了代码,可称之为cocoa的大招。
如下的例子:
使用KVC的好处
不使用KVC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | - (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
1 2 3 4 5 | - (id)tableView:(NSTableView *)tableview objectValueForTableColumn:(id)column row:(NSInteger)row { ChildObject *child = [childrenArray objectAtIndex:row]; return [child valueForKey:[column identifier]]; } |
显而易见,简化了很多代码。
KVC操作
KVC赋值
1 给当前对象属性赋值
1 | - (void)setValue:(id)value forKey:(NSString *)key; |
2给对象的属性的属性赋值
1 | - (void)setValue:(id)value forKeyPath:(NSString *)keyPath; |
3 处理未定义的键
1 | - (void) setValue:(id)value forUndefinedKey:(NSString *)key |
4 字典转模型:会为我们把和dictionary的key名字相同的class proerty设置上dict中key对应的value
1 | - (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues; |
注意:要求字典中的key和对象属性一样,都是基本的OC数据类型:Array/Dictionary/Boolean/Data/Number/String
KVC取值
1 获取对象属性的值
1 | - (id)valueForKey:(NSString *)key; |
2 获取对象属性的属性的值
1 | - (id)valueForKeyPath:(NSString *)keyPath; |
例子:
1 2 3 4 5 6 7 8 9 10 11 | 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
1 2 3 4 5 6 7 | @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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | @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中的关键字
如果将键age换成了id
会抛出异常:
*** Terminating app due to uncaught exception 'NSUnknownKeyException',reason: '[setValue:forUndefinedKey:]: this class isnot key value coding-compliant for the key id.
重写以下方法即可,处理未定义的键
1 | - (void)setValue:(id)value forUndefinedKey:(NSString *)key; |
解决方式:
1 2 3 4 5 6 | - (void) setValue:(id)value forUndefinedKey:(NSString *)key { if([key isEqualToString:@id]) key = @age; [super setValue:value forKey:key]; } |
字典里面还包含某些对应自定义类的字典或者数组
Person类增加了一个Car类型的属性
1 | @property (nonatomic, strong) Car * car; |
我们只需要重写以下方法
1 | - (void)setValue:(id)value forKey:(NSString *)key; |
解决方法:
1 2 3 4 5 6 7 8 9 10 | - (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)>)>
)