Key-Value Coding 键值编码

Key-Value Coding 键值编码

KVC和KVO是iOS开发中常见的两种技术,那么它们分别该如何使用,有哪些注意事项呢?在阅读了Apple的官方文档KVC Programming Guide 和KVO Programming Guide 后,将一些注意事项记录如下。

首先介绍下KVC。

下面是Apple 官方文档中对Key-value coding(以下简称为KVC)的定义:

Key-value coding is a mechanism for accessing an object’s properties indirectly, using strings to identify properties, rather than through invocation of an accessor method or accessing them directly through instance variables.

在应用中使用KVC 是一条重要设计原则,因为它是KVO、Core Data、Cocoa Binding 等技术的基础,同时也可以简化代码。

KVC 基础

key 是一个指明了一个对象的某一属性的字符串,而keypath则是将几个key 用”.”连接而成的一个长字符串。

使用KVC来设置和访问属性的值

假设有一个类MyClass,其声明如下:

那么设置属性时可以使用setValue:forKey: 或者setValue:forKeyPath: 方法,如

除此以外,还可以使用setValuesForKeysWithDictionary: 方法来一次性设置对象的所有属性。默认实现会对每一个键值对调用setValue:forKey: 方法。

需要注意的是,在setValue:forKey:setValue:forKeyPath: 方法中,如果指定的key 并不存在,那么消息的接收者就会收到一条setValue:forUndefinedKey: 消息。这个方法的默认实现是引发一个NSUndefinedExpection 异常,当然你也可以重载这个方法。

如果将一个非对象的属性(即一个标量)赋值为nil 会怎样呢?在这种情况下,接收者会给自己发送一条setNilValueForKey:消息。这个方法的默认实现是引发了一个NSInvalidArgumentException 异常。

访问属性的值可以使用valueForKey:valueForKeyPath:方法:

它和点语法的结果相同:

KVC 访问器方法

常用的访问器模式

一般来说,某属性的访问器的格式是:-<key> 或者-is<key>,而某属性的赋值器的格式是:-set<key>,其中key是属性的名字。如:

因为hidden 是非对象性的,所以如果它被设置了nil,那么就会触发setNilValueForKey:方法。我们可以这样实现这个方法:

一对多关系的属性的访问器模式

对于一对多的属性,虽然也可以使用-<key>-set<key> 这样的访问器模式,但是出于提高性能的考虑,最好实现一些额外的访问器方法。

有序的一对多关系

有序的一对多关系的通常体现是NSArray 和NSMutableArray。

不可变的有序的一对多关系的获得器方法

要支持对一个有序一对多关系的只读访问,需要实现下面几个方法:

  • -countOf<key>必须。类似于NSArray 的count 方法。
  • -objectIn<key>AtIndex:-<key>AtIndexed: 方法。这两个方法中的任意一个是必须的。它们对应着NSArray 的objectAtIndex:ObjectsAtIndexes: 方法。
  • -get<key>:range:。实现这个方法是可选的,但是会带来额外的性能收益。它对应着NSArray 的getObjects:range: 方法。

例子如下:

可变的有序的一对多关系的获得器方法

对于一个可变的有序的一对多关系,为了使它符合KVC,需要实现以下方法:

  • -insertObject:in<key>AtIndex:或者-insert<key>:atIndexes:。这两个方法中的至少一个需要被实现。它们类似于NSMutableArray 中的insertObject:atIndex:insertObjects:atIndexes:
  • -removeObjectFrom<key>AtIndex:或者-remove<key>AtIndexes:。这两个方法需要至少实现一个。它们类似于NSMutableArray 中的removeObjectAtIndex:removeObjectsAtIndexes:
  • -replaceObjectIn<key>AtIndex:withObject:或者-replace<key>AtIndexes:with<key>:可选

例子如下:

无序的一对多关系

无序的一对多关系的通常体现是NSSet和NSMutableSet。

不可变的无序的一对多关系的获得器方法

要支持对一个无序一对多关系的只读访问,需要实现下面几个方法:

  • -countOf<key>必须。这个方法对应着NSSet 的count 方法。
  • -enumeratorOf<key>必须。对应着NSSet 的objectEnumerator 方法。
  • -memberOf<key>必须。对应着NSSet 的member: 方法。
可变的有序的一对多关系的获得器方法

对于一个可变的无序的一对多关系,为了使它符合KVC,需要实现以下方法:

  • -add<key>Object:或者-add<key>:。这两个方法中的至少一个需要被实现。它们类似于NSMutableSet的addObject:方法。
  • -remove<key>Object:或者-add<key>:。这两个方法中的至少一个必须被实现。它们类似于NSMutableSet的removeObject:方法。
  • -intersect<key>:方法。可选的。类似于NSSet的intersectSet:方法。

键值验证

键值验证的目的是为了实现对传入值的测试,以决定是否需要接受此值,或者是用新值代替,或者是拒绝此值。

验证方法的命名传统

验证方法的命名格式是validate<key>:error:。例子如下:

实现一个验证方法

验证方法接受两个参数:一个是需要验证的对象,一个是用来返回错误信息的NSError 实例。

验证方法执行时可能有三种情况:

  1. 对象是有效的,所以返回一个YES。
  2. 对象是无效的,并且有效值无法被创建,因此返回NO。
  3. 一个新的有效对象被创建。在将参数指向这个新的对象后,返回一个YES。

调用验证方法

可以直接调用验证方法,也可以使用validateValue:forKey:error: 来间接调用。这个方法的默认实现是在接收者的方法列表中寻找名字符合validate<key>:error:的验证方法。如果找到了这个方法,就会调用这个方法。如果没有找到符合这个名字的方法,那么validateValue:forKey:error:就会返回YES。

确保满足KVC

属性以及一对一关系

  • 实现名为-<key>, -is<key> 的方法,或者拥有名为<key>_<key> 的实例变量。
  • 如果是可变的,那么实现-set<key>: 方法。
  • -set<key>: 方法中不能进行验证。
  • 如果可以的话,实现-validate<key>:error: 方法。

有序的一对多关系

  • 实现一个返回值为数组的名为-<key> 的方法或者拥有一个名为<key>_<key> 的数组实例变量。
  • 实现-countOf<key>,及-objectIn<key>AtIndex:-<key>AtIndexes: 中的一个。
  • 可以选择实现-get<key>:range 来提高性能。

对于可变的有序一对多关系,需要:

  • 实现-insertObject:in<key>AtIdex:-insert<key>:atIndexes: 中的一个。
  • 实现-removeObjectFrom<key>AtIndex:-remove<key>AtIndexes: 中的一个。
  • 可以选择实现-replaceObjectIn<key>AtIndex:withObject:-replace<key>AtIndexes:with<key>: 中的一个来提高性能。

无序的一对多关系

  • 实现一个返回值为集合的名为-<key> 的方法或者拥有一个名为<key>_<key> 的集合实例变量。
  • 实现-countOf<key>-enumerator<key>-memberOf<key>方法。

对于可变的无序一对多关系,需要:

  • 实现-add<key>Object:-add<key>: 的一个或两个。
  • 实现-remove<key>Object:-remove<key>: 的一个或两个。
  • 可以选择实现-intersect<key>:-set<key>: 方法来提高性能。

集合操作符

集合操作符是一些特殊的keypath,它们可以被当作参数传到valueForKeyPath: 方法中。

@avg

@count

@max,@min

@sum

@distinctUnionOfObjects 返回一个数组,数组元素中的重复值被去除。

@unionOfObjects返回一个数组,数组元素中的重复值不会被去除。

@distinctUnionOfArrays@unionArrays 以及@distinctUnionOfSets 与前面几个类似,因此不再赘述。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据