What is the difference between valueforKey:, objectForKey:, and valueForKeyPath:?

valueForKey: is part of the NSKeyValueCoding protocol and is therefore part of the key-value coding framework, which allows you to access class properties by name at runtime. That’s how NIBs are loaded, for example — the names of properties associated with connections are loaded and then the values are set directly by name. This contrasts with the way that visual interface design tools often work in other languages, generating lots of hidden statically compiled code.

objectForKey: is defined on dictionaries only, and looks up an object by its key. An NSDictionary is a class that stores connections between values and keys.

So, valueForKey: could be used on an NSDictionary to return meta information about the dictionary, such as the count of objects inside it, the list of all keys, etc. objectForKey: would be used actually to look into the dictionary.

At runtime, the difference is that objectForKey: is a method with a completely opaque implementation. valueForKey: explicitly relies on subsequently calling named getters and setters. The reason for the latter is that you can extend key-value coding to key-value observing, where you ask to be informed every time a particular property on a particular object changes. At runtime that’s achieved with a method swizzle, where the original setter is replaced by a new one that calls the previous setter and then sends out the required messages. Because all messages are dispatched dynamically, that’s just achieved by modifying tables within the object instance.

So any object that is key-value coding compliant (which just means declaring and implementing your properties in the proper way, which the new-ish @property/@synthesize syntax does automatically) can be observed without the object itself having to implement any code.

There’s further Apple stuff that uses key-value coding to achieve various things, including CoreData, but it’s not specifically to enable any one other technology.

valueForKeyPath: is like valueForKey: except that it can traverse several objects. So you can have a root object with a bunch of properties, each of those properties is another object with another bunch of properties, etc, and using a key path you can retrieve a value way out at the leaf of that data structure rather than having to iterate through object after object for yourself.

In summary, valueForKey: and valueForKeyPath: provide information about object instances and interact with the dynamic nature of the Objective-C runtime. objectForKey: is a dictionary specific method that does dictionary tasks.

Additions:

An example, coded as I type and assuming that NSDictionary is key-value coding compliant:

NSDictionary *someDictionary;
// create someDictionary, populate it, for example (note: we assume photoOfKeys.jpg
// definitely exists, not a good idea for production code — if it doesn't we'll get
// a nil there and anything after it won't be added to the dictionary as it'll appear
// that we terminated the list):
someDictionary = @{ @"favouriteGarment": @"hat",
                    @"@allKeys"        : [NSImage imageNamed:NSImageNameDotMac],
                    @(2)               : NSArray.new };

NSObject *allKeys; 
// we make no assumptions about which type @allKeys will be,  but are going to assume
// we can NSLog it, so it needs to be a descendant of NSObject rather than 'id' so as 
// to definitely respond to the 'description' message — actually this is just compile
// time semantics, but if someone else reads this code it'll make it obvious to them 
// what we were thinking...

// some code to get all of the keys stored in the dictionary and print them out;
// should print an array containing the strings 'favouriteGarment', '@allKeys' and
// the number 2
allKeys = [someDictionary valueForKey:@"@allKeys"];
NSLog(@"%@", allKeys);

// some code to get the object named '@allKeys' from the dictionary; will print
// a description of the image created by loading photoOfKeys.jpg, above
allKeys = [someDictionary objectForKey:@"@allKeys"];
NSLog(@"%@", allKeys);
// `objectForKey is analogous to `objectForKeyedSubscript:`, aka
allKeys = someDictionary[@"@allKeys"];

allKeys is a property of NSDictionary as described here. I’ve also added a mapping from the NSString allKeys to a photograph of some keys. Whether I use the key-value coding valueForKey: methods or the NSDictionary objectForKey: lookup method dictates whether I read the property of the object instance or whether I send the object instance a message asking it to do its unique job.

Leave a Comment