Java Perspective: Key-Value Protocols, Behind the Magic of Mac OS X Development
- Like Reflection, Sort Of
- Keypaths
- Key Value Observing
- Dependent Keys
- Conclusion
For more information on Java development, visit our Java Reference Guide or sign up for our Java Newsletter.
Every Java developer has learned some shortcuts to create the dreaded getters and setters. Some use an IDE that produces these necessary but frustrating methods; some use cut and paste, complicated macros, or (worse) write them by hand. Some other languages have figured out ways to eliminate the necessity of writing getters and setters by generating them automatically or simply recommending accessing the fields directly.
Cocoa/Objective-C has another solution to the ugliness that is getters and setters. This solution is Key Value Coding and Key Value Observing, KVO and KVC for short.
Like Reflection, Sort Of
Starting with the basics, KVC is a way of accessing the fields stored in a class instance in a way that is very similar to Java's Reflection API. Take, for example, the following Objective-C header:
#import <Cocoa/Cocoa.h> @interface ExampleObject : NSObject { NSString *value1; NSString *value2; NSString *value3; NSArray *array1; BOOL flagValue; } - (NSString *)value1; - (NSString *)value2; - (BOOL)isFlagValue; - (NSArray *)array1; - (void)setValue1:(NSString *)newValue; - (void)setvalue2:(NSString *)newValue; - (void)setFlatValue:(BOOL)newFlag; - (void)setArray1:(NSArray *)newArray; @end
Normally, to access any of these values you would send a message to the object such as [reference value1], which would return a pointer to value1. However, with KVC you instead send a message to the Key-Value protocol as follows:
[reference valueForKey:@"value1"];
This method will return the pointer to value1 inside of the object reference. In addition to being able to send messages to the proper getters, this method will also look for instance variables inside of the receiving object that match the name passed in. Therefore, this same method can reference value3 in the header above:
[reference valueForKey:@"value3"];
Thus, both of them are accessed the same way, even though one has an accessor method, and the other does not. In addition to the straightforward accessor methods and instance variables, this method can also resolve isXXX properly. Therefore, the following will access the method isFlagValue instead of accessing the instance variable directly:
[reference valueForKey:@"flagValue"];
The above method will return an NSNumber instead of a BOOL. Because BOOL is a primitive and not a class, it is auto-boxed into an NSNumber.
It is also possible to set values using this same protocol. Instead of calling the following:
[reference setValue1:@"test"];
an instance variable would be set as follows:
[reference setValue:@"test" forKey:@"value1"];
Although this is a bit more long-winded then calling the setter methods directly, it is more consistent and allows direct access to the instance variables within an object without knowing whether there is an accessor method. In addition, if an accessor method is either added or removed at some later date, it does not require any additional code changes.