介绍
Key-Value Coding简称KVC,中文名为键值编码。它是一种利用字符串间接访问对象属性的方法。而这个字符串便就是键。访问器,即setter和getter,也是一种间接访问对象属性的方法,只不过在有些场合更加适合使用KVC。虽然KVC在业务逻辑中很少会使用,但在Key-Value Observing,Core Data, Cocoa bindings, scripatability这些模式或者高级特性的基础,因此KVC很重要。
在如下场合,利用KVC明显比利用访问器来得方便。假设一个要从model层获取所有联系人显示到NSTableView上(Cocoa里的列表)。最普通的方式如下:
- (id)tableView:(NSTableView *)tableview
objectValueForTableColumn:(id)column row:(NSInteger)row {
Person *person = [contact objectAtIndex:row];
if ([[column identifier] isEqualToString:@"name"]) {
return [person name];
}
if ([[column identifier] isEqualToString:@"age"]) {
return [person age];
}
if ([[column identifier] isEqualToString:@"favoriteColor"]) {
return [person favoriteColor];
}
// And so on.
}
仔细想想,如果一个person的属性很多,那么代码会变得十分庞大,并且很难维护,如果利用KVC则会简洁得多
- (id)tableView:(NSTableView *)tableview
objectValueForTableColumn:(id)column row:(NSInteger)row {
Person *person = [contact objectAtIndex:row];
return [person valueForKey:[column identifier]];
}
PS: 这里不理解KVC没关系,可以看完后面的实现,再来体会这里。
相关术语
在介绍如何实现KVC之前,先介绍几个接下来要使用的术语:
- attribute. 它是一个简单属性,不存在一对一关系,一对多关系,只是描述某个事物的一个方面,不和其他对象产生任何联系。例如:标量,字符串,布尔值,NSNumber以及其他不可变的对象如NSColor等。
- to-one relationship. 它反映的一对一关系属性,与之对应的对象和它独立变化。例如:UIView的superView就是一个to-one relationship属性。
- to-many relationship. 它反映的是一对多属性关系,通常用集合来表示这样的关系。例如:NSArray, NSSet等。当然也可以利用后面介绍的方法,来自定义KVC的访问方式,在宏观上反映出一对多属性关系。
- key. key就是一个唯一标识对象属性的字符串。通常就是属性名,如person的name属性,则key=”name”。当然可以自定义,它与具体KVC的访问方法的实现或者实例变量有关,后面会介绍。
- key path. key path是利用“.”分割的一串字符串,用来标识一系列对象属性的层层穿越。例如一个国家有好多城市,一个城市有很多街道,则如果我拥有国家这个对象country,要访问到这个国家的中的某条街道,则key path=”country.city.street”
具体实现
获取attribute简单属性的方法
[object valueForKey: @"keywords"];
object: 拥有属性的对象。
keywords: 唯一标识属性的字符串key。
举例,例如要访问一个人person的名字name属性.
[person valueForKey: @"name"];
当然除了这样,还需要通过指定对应的访问器,来将key和属性产生关系。
-(void)setName: (NSString *)name
{
self.name = name;
}
-(NSString *)name
{
return self.name;
}
看起来和setter,getter几乎是一模一样的,没错,就是一模一样。KVC的访问器,和关联属性的key有关。其内容在后面一节介绍。这里你仅需知道,它不是一般意义上的setter,getter访问器。
如果没有指定KVC访问器,那么接收对象会给自己发一个valueForUndefinedKey:
消息。而这个消息在NSKeyValueCoding
中定义,其默认实现是抛出一个NSUndefinedKeyException
异常。你可以通过重写这个方法,来达到自定义需求。
相类似的,利用key path实现:
[object valueForKeyPath: @"parentKey.subKey"];
object: 拥有属性的对象。
parentKey.subKey: 唯一标识属性路径的字符串key path。
举个例子,访问一个国家的某个城市的,某个街道
[country valueForKeyPath: @"city.street"];
在key path的每个子路径都需要实现与之对应的KVC访问器,如果其中任何一个没有与之对应的访问器,则出出发valueForUndefinedKey:
消息。
PS: 如果country的城市属性是一个集合citys,而每个城市又有街道的集合streets,则通过”citys.streets”这个key path会返回这个国家中所有城市的所有街道。
当然如果你想在一个语句中获取多个属性值,则利用dictionaryWithValuesForKeys:
方法会返回一个NSDictionary,里面包含属性的key和对应属性的值。
PS: 在集合对象中,NSArray, NSSet, NSDictionary不能包含nil来表示空值,因为nil表示集合结束。如果要表示空值,请用NSNull,NSNull是一个单例类。[NS