0

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?

Community
  • 1
  • 1
MartinW
  • 4,966
  • 2
  • 24
  • 60

3 Answers3

3

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:

  1. 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).

  2. 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.

  3. 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:.

  4. 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.

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

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • Was there ever a `get` naming convention in Objective-C? I know that it is the convention for when there are multiple return arguments but that wouldn't really work with KVC. – David Rönnqvist Oct 16 '14 at 19:38
  • I suspect that KVC was implemented before the naming convention was firmly established, or that Apple just wanted to make it easier to use KVC even for developers who didn't follow the naming convention. – rob mayoff Oct 16 '14 at 19:39
1

This is parallel to the fact that you can call these methods using property syntax:

BOOL ok = aThing.alwaysYES

In that case and in your case, exactly the same thing happens: the first thing the runtime tries is to treat this as a getter method. What you've written is a getter method.

As for your question "can I safely use it", safely yes, but what you're doing is kind of silly, since you know (and have declared) that these methods exist. KVC is about probing to see whether methods exist. If you have a reason to specify one of these methods by string name, there are better ways to call it than using KVC.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • First, I am just trying to understand :) And then I use it to enable menu items vie Interface Builder’s *enabled* binding. In this case it seems useful to me? – MartinW Oct 16 '14 at 19:35
  • If you have a question about menu item enablement through bindings, ask a question about that. That's not the question you asked here; the question you _did_ ask has been answered, I think. You've been told why it works and where it's documented and how to understand it. – matt Oct 16 '14 at 19:38
  • Yes, and I am very happy about it and thank you for the answer. The question about menu item enabling is linked in the post: http://stackoverflow.com/questions/26141296/is-it-ok-to-send-normal-messages-via-interface-builders-binding-s-model-key-pat – MartinW Oct 16 '14 at 19:40
0

Properties are nothing special at runtime; they generate a getter and setter (if not readonly) which conforms to KVC; for example:

@property NSString *aString;

will generate:

- (NSString)aString {
   ...
}

- (void)setAString(NSString *string) {
   ...
}

just as if you had declared those methods in the header (which itself is optional).

See the Key Value Coding Fundamentals documentation.

Droppy
  • 9,691
  • 1
  • 20
  • 27