Key Value Coding (KVC) is one of the cornerstones of Cocoa Bindings. KVC allows us to access the attributes of an object without calling the accessors of that object directly. Key Value Coding is implemented through an informal protocol on NSObject itself and is used mainly through the getter/setter pair ‑valueForKey: and ‑setValue:forKey:.
The method ‑valueForKey: is a generic accessor that retrieves an attribute on an object. For example, if we had an object called Recipe and it had an attribute called name, normally we’d access that attribute via the following:
| Recipe *myRecipe = ... |
| NSString *recipeName = [myRecipe name]; |
However, this requires specific knowledge about the Recipe object to exist in the calling method and generally requires that we import the header file of the Recipe object. However, with Key Value Coding, we can obtain this same attribute without any preexisting knowledge about the Recipe object.
| id myRecipe = ... |
| NSString *recipeName = [myRecipe valueForKey:@"name"]; |
By itself, this isn’t all that useful. However, there are huge benefits that aren’t obvious on the surface. Here’s an example of how you might better take advantage of this ability:
| - (NSString*)description |
| { |
| NSMutableString *string = [NSMutableString stringWithFormat:@"[%@] {", |
| [self class]]; |
| NSEntityDescription *desc = [self entity]; |
| for (NSString *name in [desc attributeKeys]) { |
| [string appendFormat:@"\n\t%@ = '%@'", name, [self valueForKey:name]]; |
| } |
| [string appendString:@"\n}"]; |
| return string; |
| } |
In this example, we utilize the NSEntityDescription class (discussed in greater detail in Chapter 2, Under the Hood of Core Data) to retrieve the names all of the attributes of an NSManagedObject subclass and generate an NSString for display in the logs. With this method, we can reuse it in every NSManagedObject subclass that we create, rather than having to create a custom ‑description method for each subclass.
There are a couple of things to note in this example. First, the target object isn’t required to have accessor methods for the attribute being queried. If our target object has only an ivar or property for a name, it will still be resolved and retrieved properly. (ivar stands for instance variable, which is different from a static or local variable.) In addition, if the target object has neither an accessor nor an ivar, the target object will still have a chance to respond to the request before an error occurs via the ‑valueForUndefinedKey: method. Lastly, all the properties of an NSManagedObject are queryable via the KVC protocol. What this means is if we have an NSManagedObject defined in our model, we can retrieve an instance of that object and access its properties without having to implement a single line of code in the target object!
Dynamically accessing properties on an object is a useful skill, but it’s only half of what KVC does. The other half is the ability to dynamically set attributes on an object in much the same manner that we can retrieve them. Normally, we’d change the name attribute on an Recipe object by calling the setter method.
| Recipe *myRecipe = ... |
| [myRecipe setName:@"Yummy Cookies"]; |
As in the earlier getter accessor, preexisting knowledge of the Recipe object is required in order to use that accessor without compiler warnings. However, with KVC, we can access it in a more dynamic manner.
| id myRecipe = ... |
| [myRecipe setValue:@"Yummy Cookies" forKey:@"name"]; |
This call attempts to use the setter ‑setName: if it’s available; if it isn’t, the call will look for and use the attribute directly if it’s available, and failing that, it will call ‑setValue:forUndefinedKey: on the target object. The combination of the dynamic getter and the dynamic setter allows us to manipulate objects without having to write accessors and without having to know (or care!) if they exist. This is used to great effect in one of the Core Data recipes to create a preferences singleton object that reads its values from a properties table. See Chapter 12, Dynamic Parameters.
In addition, as of OS X 10.5 Leopard, we have the keyword @property, which allows us to synthesize accessors to attributes on an object. This feature plays very nicely with KVC, and the two can be used together to produce extremely dynamic and flexible code. By utilizing the @property keyword, we can instruct the compiler to generate the ivar, getter and setter accessors that are KVO-compliant. For example, if we had an object with the following header:
| @interface MyObject : NSObject |
| |
| @property (strong) NSString *myString; |
| |
| @end |
Xcode would interpret it the same as the following header:
| @interface MyObject : NSObject |
| { |
| NSString *myString; |
| } |
| |
| - (NSString*)myString; |
| - (void)setMyString:(NSString*)string; |
| |
| @end |
With the @property keyword the compiler will automatically generate the accessors for us. Thus the resulting implementation would look similar to the following:
| #import "MyObject.h" |
| |
| @implementation MyObject |
| |
| - (NSString*)myString; |
| { |
| return myString; |
| } |
| |
| - (void)setMyString:(NSString*)string; |
| { |
| @synchronized(self) { |
| if ([string isEqualToString:myString]) return; |
| myString = string; |
| } |
| } |
| |
| @end |
With ARC (Automatic Reference Counting), the compiler will also handle the memory management of the properties for us. If we’d defined the property as weak, then the compiler wouldn’t retain the value but merely assign it. Likewise, the locking of the ivar is a default option that we could’ve turned off by adding the nonatomic option to the property definition.
When dealing with multiple properties on an object, this can be a great time-saver. There have also been indications that the accessors generated by the compiler are faster than the “normal” accessors that developers write. In addition to generating accessors, the compiler is smart about what it implements. If we need to implement our own setter for a property, it won’t overwrite that setter.
If we wanted to have the accessors be different than the name of the underlying ivar, we can add the @synthesize keyword to the implementation file:
| @implementation MyObject |
| |
| @synthesize myString = _myString; |
| |
| @end |
The @synthesize keyword was required in previous generations of the compiler, but it’s now optional and only used when the ivar needs to be different than the accessors.
Alongside the @synthesize property, we also have the @dynamic property. The @dynamic property tells the compiler that while the accessors for the property aren’t there at compile time, they will be at runtime, and it instructs the compiler not to produce a warning for them. @synthesize and @dynamic are sibling keywords. For each property, we can use one or the other but not both. However, neither is required in a situation where we’re implementing the accessors ourselves. If the accessor methods will be implemented at runtime, we’d use the @dynamic property instead of the @synthesize property so that the compiler doesn’t produce a warning. This is particularly useful for Core Data subclasses.