NSObject
提供的NSKeyValueCoding
协议的默认实现使用一组明确定义的规则将基于key
的访问调用映射到对象的属性。 这些协议方法使用参数key
来搜索自己对象的访问方法、实例变量和遵循特定命名规则的相关方法。 尽管我们很少修改此默认搜索,但了解它的工作原理可能很有帮助,既可以用于跟踪键值对象的行为,也可以用于使自己的对象符合规范。
valueForKey: 的基本搜寻模式
valueForKey:
方法的默认实现,给定一个参数key
作为输入,执行下面的步骤
依次按照
get
, 或者, , is _
的名称顺序来搜索实例的访问方法。找到的话,调用该方法并跳转到步骤5
并显示结果。否则进行下一步。-
如果没有找到,查询实例中名称匹配
countOf
和objectIn
(与AtIndex: NSArray
定义的方法相对应) 以及
(与AtIndexes: NSArray
的objectsAtIndexes:
方法相对应) 的方法。如果找到第一个方法以及其他两个方法中的至少一个,创建一个响应所有
NSArray
方法的集合代理对象并返回。否则,跳转到步骤三
发送到集合代理对象的每个
NSArray
消息都会导致-countOf
,-objectIn
和AtIndex: -
消息被发送到AtIndexes: -valueForKey:
的原始接收者。如果receiver
还实现了名称类似get
的可选方法, 在合适的时候会使用该方法以获得最佳性能。:range: -
如果前两步都没有成功,查找名为
countOf
和, enumeratorOf , memberOf
(与: NSSet
类定义的原始方法相对应)的方法。如果三个方法都找到,创建一个响应所有
NSSet
方法的集合代理对象并返回它。否则跳转到步骤四
。发送到集合代理对象的每个
NSSet
消息将导致-countOf
,-enumeratorOf
和-memberOf
消息被发送到: -valueForKey:
的原始接收者。 -
如果前几步都没有成功,并且
receiver
类方法accessInstanceVariablesDirectly
返回YES
,依次查询名称为_
,_is
,
, 或is
的实例变量。如果找到了,直接获取到该实例变量的value
并跳转到步骤五
。否则,跳转到步骤六
。accessInstanceVariablesDirectly
默认返回YES
如果检索到的属性值是一个对象指针,直接返回结果。如果该值是
NSNumber
支持的基本数据类型,将其存储在NSNumber
实例中并返回,如果该值是NSNumber
不支持的基本数据类型,将其转为NSValue
对象并返回。如果都失败了,调用
valueForUndefinedKey:
方法,该方法默认抛出一个异常,但是NSObject
子类可以提供特定行为。
setValue:forKey: 的搜索模式
setValue:forKey:
方法的默认实现,给定key
和value
参数作为输入,给指定key
的属性设置value
。receiver
将执行以下过程:
依次查找名为
set
或者: _set
的方法,如果找到,用输入值value
(或根据需要解包)调用它并完成。如果没有找到访问器,并且类方法
accessInstanceVariablesDirectly
返回YES
,依次查找名为_
,_is
,
, 或is
的实例变量。如果找到,直接设置变量的值为value
并完成。如果以上都没有找到,会调用
setValue:forUndefinedKey:
方法,默认会抛出异常,但是NSObject
的子类可以覆盖它。
比如一个Person
类:
// .h 文件
@interface Person : NSObject
@end
// .m文件方法
- (void)setName:(NSString *)name
{
NSLog(@"setName: %@", name);
}
- (void)_setName:(NSString *)name
{
NSLog(@"_setName: %@", name);
}
我们不在.h
文件中声明name
属性,而只是直接在.m
文件中实现了- (void)setName:(NSString *)name
方法。
[person setValue:@"John" forKey:@"name"];
这样调用会执行.m
的setName :
方法,而不是抛出一个setValue:forUndefinedKey
异常。
mutableArrayValueForKey的搜索模式
mutableArrayValueForKey:
的默认实现是,给定参数key
做为输入,为名为key
的属性返回一个可变的代理数组,步骤为:
-
查询一组名为
insertObject:in
和AtIndex: removeObjectFrom
(分别与AtIndex: NSMutableArray
的insertObject:atIndex:
和removeObjectAtIndex:
相对应)的方法,或者名称类似insert
和:atIndexes: remove
(与AtIndexes: NSMutableArray insertObjects:atIndexes:
和removeObjectsAtIndexes:
方法相对应 ) 的方法。如果该对象有至少一个插入方法和至少一个删除方法,返回一个代理对象, 发送给代理对象的每个
NSMutableArray
消息将导致-insertObject:in
,AtIndex: -removeObjectFrom
,AtIndex: -insert
, 和:atIndexes: -remove
消息发送给AtIndexes: mutableArrayValueForKey:
的receiver
。当收到
mutableArrayValueForKey:
消息的对象也实现了一个可选的替换对象方法,名称如replaceObjectIn
或者AtIndex:withObject: replace
,该代理对象也会在适当的时候调用它们以获取最佳的性能。AtIndexes:with : -
如果对象不具有可变数组方法,则查找名称匹配
set
模式的方法。这种情况下,返回一个代理对象,该对象通过向: mutableArrayValueForKey:
的接收者发送set
消息以响应: NSMutableArray
消息。这一步描述的机制比上一步的效率更低,因为他可能重复创建新集合对象而不是修改已经存在的。因此,当设计自己的KVC兼容对象时通常应该避免使用它。
-
如果以上都没有找到,并且
receiver
的类方法accessInstanceVariablesDirectly
返回YES
,依次查询名称类似_
或
的实例变量。如果找到这样一个实例变量,则发送到集合代理对象的每个NSMutableArray消息都将被转发给实例变量,因此它通常必须是
NSMutableArray
的实例或NSMutableArray
的子类。 -
如果都失败了,只要收到
NSMutableArray
消息,就返回一个可变集合代理对象,该对象发出setValue:forUndefinedKey:
消息给mutableArrayValueForKey:
消息的原始接收者。setValue:forUndefinedKey:
方法的默认实现会抛出NSUndefinedKeyException
异常,
mutableOrderedSetValueForKey:的搜索模式
mutableOrderedSetValueForKey:
的默认实现与valueForKey:
大致相同,但是始终返回一个可变的集合代理对象,而不是valueForKey
返回的不可变集合。另外,它还执行以下操作:
-
查找名称类似
insertObject:in
和AtIndex: removeObjectFrom
(对应于由AtIndex: NSMutableOrderedSet
类定义的两个最原始的方法) 的方法,以及insert
和:atIndexes: remove
(对应于AtIndexes: insertObjects:atIndexes:
和removeObjectsAtIndexes:
) 方法。如果找到至少一个插入方法和至少一个删除方法,返回一个代理对象,当该代理对象收到
NSMutableOrderedSet
消息时会向mutableOrderedSetValueForKey:
消息的receiver
发送insertObject:in
,AtIndex: removeObjectFrom
,AtIndex: insert
, 和:atIndexes: remove
消息组合。AtIndexes: 该代理对象也会使用名称类似
replaceObjectIn
或者AtIndex:withObject: replace
的方法。如果原始对象中存在这些方法的话。AtIndexes:with : -
如果可变集合方法没有找到,查找名称类似
set
的方法。这种情况下,返回的代理对象每次收到: NSMutableOrderedSet
消息时都会向mutableOrderedSetValueForKey:
的receiver
发送set
消息。: 本步骤描述的机制比上一步的效率低得多,因为它可能涉及重复创建新的集合对象,而不是修改现有的集合对象。 因此,在设计自己的KVC对象时通常应该避免它。
如果以上都没有找到,并且
receiver
的accessInstanceVariablesDirectly
类方法返回YES
,依次查找名为_
或者
的实例变量。如果找到了,返回的代理对象将它收到的任何NSMutableOrderedSet
消息转发给该实例变量的value
,因此它通常是NSMutableOrderedSet
或其子类的实例。如果都失败了,每当返回的代理对象收到可变集合的消息时就会向
mutableOrderedSetValueForKey:
的原始receiver
发送setValue:forUndefinedKey:
消息。setValue:forUndefinedKey:
的默认实现会抛出NSUndefinedKeyException
异常。
Mutable Sets的搜索模式
mutableSetValueForKey:
的默认实现是给定一个参数key
作为输入,为名为key
的数组属性返回一个可变的代理集合,步骤为:
-
查找名称类似
add
和Object: remove
(分别对应Object: NSMutableSet
的addObject:
和removeObject:
)方法以及add
和: remove
(对应于: NSMutableSet
的unionSet:
和minusSet:
)方法。如果找到至少一个添加方法和至少一个删除方法,返回一个代理对象,该代理对象向mutableSetValueForKey:
的原始接收者发送add
,Object: remove
,Object: add
, 和: remove
消息,用于接收每个: NSMutableSet
消息。该代理对象也会使用名称类似
intersect
或: set
的方法以获得最好的性能,如果这些方法存在的话。: 如果
mutableSetValueForKey:
调用的接收者是一个managed
对象(NSManagedObjectModel
的实例),那么搜索模式不会像针对非managed
对象一样继续。-
如果可变集合方法没有找到,并且该对象也不是一个
managed
对象,查找名为set
的访问器方法。如果找到了该方法,返回的代理对象每收到: NSMutableSet
消息时,都会将-set
消息发送到: mutableSetValueForKey:
的原始接收方。本步骤描述的机制比第一步的效率低得多,因为它可能涉及重复创建新的集合对象,而不是修改现有集合对象。 因此,在设计自己的键值编码兼容对象时通常应该避免它。
如果以上都没有找到,并且
accessInstanceVariablesDirectly
类方法返回YES
,则依次查找名称为_
或者
的实例变量。如果找到了类似的实例变量,代理对象将它收到的NSMutableSet
消息转发给该实例变量(通常是NSMutableSet
或其子类的实例)。如果都失败了,返回的代理对象通过向
mutableSetValueForKey:
消息的原始接收者发送setValue:forUndefinedKey:
消息以响应它收到的任何NSMutableSet
消息。
参考资料
- 苹果开发文档