Originally posted by swcrissman:
<STRONG>So, say I have a date member variable in my target class, I can call [target valueForKey: @"date"] and have it return the date for me. Now, does the valueForKey method actually get the member variable, or does it return the return value of the getter function of the same name?
</STRONG>
valueForKey: first looks for a method by that name, and if it's not there, looks for the instance variable of that name. So, if you have a method, it will be called. [If a class overrides +accessInstanceVariablesDirectly to return NO, then valueForKey: will not look for instance variables in objects of that class. I don't think any existing classes override this though.]
The methods are declared in NSKeyValueCoding.h, and are documented in /System/Library/Frameworks/Foundation.framework/Resources/English.lproj/Documentation/Reference/ObjC_classic/Protocols/NSKeyValueCoding.html
<STRONG>
Do all classes that derive from NSObject have this function? If so, why must we always implement getters, rather than call this? Overhead?
</STRONG>
Yes, the methods are implemented on NSObject, so all classes that inherit from NSObject have it (which is so say basically every object).
The key value coding methods are more intended to be used by generic, abstract code that doesn't know what kind of classes it's dealing with in advance -- valueForKey: offers a way for code to call methods on a class when it doesn't know what the class' methods. It can open up the door for some powerful abstractions, but isn't really intended for run-of-the-mill code.
If you're writing code where you already know which classes we're dealing with, it makes much more sense to use the provided API. When using valueForKey:, the compiler can do no type-checking. If you misspell the string, you won't find out until runtime, rather than having the compiler catch it for you. If the method returns an NSDate but you accidentally assign it to an NSString variable, the compiler won't be able to catch it, and again you'll be left to find out at runtime when your code tries to call an NSString method on the value. And while valueForKey: is pretty fast, it's definitely not as fast as calling the method directly. In short, you'd be losing a staggering amount of compile-time type checking plus some performance by using valueForKey: everywhere, where the only benefit is not having to write a few accessor methods. While it would work, it's not worth it.
For an example of where valueForKey: is useful, you could look at MiscMergeKit, currently distributed with my autodoc tool (www.misckit.com). It can read in a text template, with certain sections specially marked as "keys". When executed, an object (any object) is provided at runtime, and the marked sections are replaced with the -valueForKey: for the string. For example, "Dear <<fullName>>: In case you have forgotten your password, the password hint you originally provided is <<passwordHint>>. If you still can't remember your password, ...". At runtime, the -fullName and -passwordHint method will be called on the provided object, and the returned values placed in the final result. [Actually, -valueForKeyPath: is used, where a key like "foo.bar.baz" is turned into successive -valueForKey calls. MiscMerge also has looping constructs and other stuff.]
Before -valueForKey:, MiscMerge required the use of dictionaries rather than working on any object. To use it, the coder had to build up dictionaries with all values needed by the template. With -valueForKey:, the original objects -- no matter which class -- can just be used directly, and have its methods directly called. Autodoc uses it to produce class documentation -- it parses ObjC code and stores the data into some internal objects, then it runs those objects through a template to produce the documentation (different doc formats are supported by using different templates).
Apple uses -valueForKey: in a similar situation; to help with AppleScript support in the Cocoa frameworks. Much of the EOF and WebObjects products are also based around it; most of the abstractions and functionality they provide would not have been possible without it.