使用任意方法进行键值编码,而不仅仅是属性(Key-Value-Coding with arbitrary methods, not only properties)

似乎-valueForKey:和-valueForKeyPath:使用任意方法, 而不仅仅是属性 。 这看起来很方便:

我首先在Interface Builder中偶然发现它 ,然后做了一些实验:

// Thing.h #import <Foundation/Foundation.h> @interface Thing : NSObject - (BOOL) alwaysYES; - (BOOL) alwaysNO; @end // Thing.m #import "Thing.h" @implementation Thing - (BOOL) alwaysYES { return YES; } - (BOOL) alwaysNO { return NO; } @end

我可以通过-valueForKey:和-valueForKeyPath:调用这些方法-valueForKeyPath: 尽管它们是普通的方法而且没有属性

Thing *aThing = [[Thing alloc] init]; id result; result = [aThing valueForKey:@"alwaysYES"]; NSLog(@"result is: %@", result); result = [aThing valueForKeyPath:@"alwaysNO"]; NSLog(@"result is: %@", result);

编译,运行并给出正确的结果。 这在任何地方记录? 我可以安全地使用它吗? 我怎么能理解呢?

It seems that -valueForKey: and -valueForKeyPath: work with arbitrary methods, not only with properties. This seems very convenient:

I first stumbled upon it in Interface Builder, and then made some experiments:

// Thing.h #import <Foundation/Foundation.h> @interface Thing : NSObject - (BOOL) alwaysYES; - (BOOL) alwaysNO; @end // Thing.m #import "Thing.h" @implementation Thing - (BOOL) alwaysYES { return YES; } - (BOOL) alwaysNO { return NO; } @end

I can call these methods via -valueForKey: and -valueForKeyPath: despite the fact that they are normal methods and no properties:

Thing *aThing = [[Thing alloc] init]; id result; result = [aThing valueForKey:@"alwaysYES"]; NSLog(@"result is: %@", result); result = [aThing valueForKeyPath:@"alwaysNO"]; NSLog(@"result is: %@", result);

Compiles, runs and gives the correct results. Is this documented anywhere? Can I safely use it? How can i understand it?

最满意答案

Cocoa的键值编码(KVC)系统比Objective-C中对显式属性(用@property声明)的支持更旧,因此KVC是根据方法而不是属性来定义的。

键值编码编程指南 ”中的“ valueForKey:默认搜索模式valueForKey: ”说明了valueForKey:如何决定做什么。 它首先查找(除其他外)一个方法,其名称正是您传递给valueForKey:的键valueForKey: 。 以下是文档中引用的完整搜索模式:

在接收器的类中按顺序搜索名称与模式get<Key> , <key>或is<Key>匹配的访问器方法。 如果找到这样的方法,则调用它。 如果方法的结果类型是对象指针类型,则只返回结果。 如果结果的类型是NSNumber支持的标量类型之一,则完成转换并返回NSNumber 。 否则,转换完成并返回NSValue 。 任意类型的结果都转换为NSValue对象,而不仅仅是NSPoint , NSRange , NSRect和NSSize类型。

否则(没有找到简单的访问器方法),在接收器的类中搜索名称与模式countOf<Key>和objectIn<Key>AtIndex:对应于NSArray类定义的原始方法)和<key>AtIndexes:对应于NSArray方法<key>AtIndexes: 。 如果countOf<Key>方法和其他两个可能方法中的至少一个,则返回响应所有NSArray方法的集合代理对象。 发送到集合代理对象的每个NSArray消息将导致countOf<Key> , objectIn<Key>AtIndex:和<key>AtIndexes:消息被发送到valueForKey:的原始接收器的某种组合valueForKey: 。 如果接收器的类还实现了一个名称与模式get<Key>:range:匹配的可选方法get<Key>:range:将在适当时使用该方法以获得最佳性能。

否则(没有找到简单的访问器方法或数组访问方法集),在接收器的类中搜索名称与模式countOf<Key> , enumeratorOf<Key>和memberOf<Key>:对应的)模式匹配的countOf<Key>方法到NSSet类定义的原始方法)。 如果找到所有三个方法,则返回响应所有NSSet方法的集合代理对象。 发送到集合代理对象的每个NSSet消息将导致countOf<Key> , enumeratorOf<Key>和memberOf<Key>:某种组合memberOf<Key>:消息被发送到valueForKey:的原始接收者valueForKey: 。

否则(没有找到简单的访问器方法或集合访问方法集),如果接收者的类方法accessInstanceVariablesDirectly返回YES ,则在接收者的类中搜索名称与模式_<key> , _<key> _is<Key>匹配的实例变量, <key>或按is<Key> ,按此顺序。 如果找到这样的实例变量,则返回接收器中的实例变量的值。 如果结果的类型是NSNumber支持的标量类型之一,则完成转换并返回NSNumber 。 否则,转换完成并返回NSValue 。 任意类型的结果都转换为NSValue对象,而不仅仅是NSPoint , NSRange , NSRect和NSSize类型。

如果上述情况均未发生,则返回结果,默认实现调用valueForUndefinedKey: .

Cocoa's key-value coding (KVC) system is older than support for explicit properties (declared with @property) in Objective-C, so KVC is defined in terms of methods, not properties.

“Default Search Pattern for valueForKey:” in the Key-Value Coding Programming Guide spells out how valueForKey: decides what to do. It starts by looking for (amongst other things) a method whose name is exactly the key you passed to valueForKey:. Here is the full search pattern, quoted from the documentation:

Searches the class of the receiver for an accessor method whose name matches the pattern get<Key>, <key>, or is<Key>, in that order. If such a method is found it is invoked. If the type of the method's result is an object pointer type the result is simply returned. If the type of the result is one of the scalar types supported by NSNumber conversion is done and an NSNumber is returned. Otherwise, conversion is done and an NSValue is returned. Results of arbitrary types are converted to NSValue objects, not just NSPoint, NSRange, NSRect, and NSSize types).

Otherwise (no simple accessor method is found), searches the class of the receiver for methods whose names match the patterns countOf<Key> and objectIn<Key>AtIndex: (corresponding to the primitive methods defined by the NSArray class) and <key>AtIndexes: (corresponding to the NSArray method objectsAtIndexes:). If the countOf<Key> method and at least one of the other two possible methods are found, a collection proxy object that responds to all NSArray methods is returned. Each NSArray message sent to the collection proxy object will result in some combination of countOf<Key>, objectIn<Key>AtIndex:, and <key>AtIndexes: messages being sent to the original receiver of valueForKey:. If the class of the receiver also implements an optional method whose name matches the pattern get<Key>:range: that method will be used when appropriate for best performance.

Otherwise (no simple accessor method or set of array access methods is found), searches the class of the receiver for a threesome of methods whose names match the patterns countOf<Key>, enumeratorOf<Key>, and memberOf<Key>: (corresponding to the primitive methods defined by the NSSet class). If all three methods are found, a collection proxy object that responds to all NSSet methods is returned. Each NSSet message sent to the collection proxy object will result in some combination of countOf<Key>, enumeratorOf<Key>, and memberOf<Key>: messages being sent to the original receiver of valueForKey:.

Otherwise (no simple accessor method or set of collection access methods is found), if the receiver's class method accessInstanceVariablesDirectly returns YES, the class of the receiver is searched for an instance variable whose name matches the pattern _<key>, _is<Key>, <key>, or is<Key>, in that order. If such an instance variable is found, the value of the instance variable in the receiver is returned. If the type of the result is one of the scalar types supported by NSNumber conversion is done and an NSNumber is returned. Otherwise, conversion is done and an NSValue is returned. Results of arbitrary types are converted to NSValue objects, not just NSPoint, NSRange, NSRect, and NSSize types.

If none of the above situations occurs, returns a result the default implementation invokes valueForUndefinedKey:.

更多推荐