• No results found

Key Value Coding

In document Core Data 2nd Edition (Page 148-153)

OS X: Bindings, KVC, and KVO

8.1 Key Value Coding

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

-valueForKey:

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 would 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 is not all that useful. However, there are huge benefits that are not 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 1, Under the Hood of Core Data, on page 1) 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 is not 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!

-setValue:forKey:

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 would 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 is available; if it is not, the call will look for and use the attribute directly if it is available, and failing that, it will call -setValue:forUndefinedKey: on the target object. The combination of the dynamic getter coupled with 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 10, Dynamic Parameters, on page 179.

@property

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 getter and setter accessors that are KVO-compliant.

In a 32-bit application, we can define the @property that has the same object type and name as a defined ivar. This will tell the compiler that getter and setter accessors exist or will exist for that ivar. In a 64-bit application, the Key Value Coding

139

ivar itself is not required because the property definition handles that for us, as well. For example, if we had an object with the following header:

@interface MyObject : NSObject {

NSString *myString; //Only required for a 32-bit application }

@property (retain) NSString *myString;

@end

Xcode would interpret it the same as the following header:

@interface MyObject : NSObject

@dynamic keywords for use in our implementation files. @synthesize will generate the actual accessors that the @property alludes to in the header. Therefore, in our example MyObject.m file, we can declare the following:

#import "MyObject.h"

@implementation MyObject

@synthesize myString;

@end

and have the same effective code as this:

#import "MyObject.h"

The compiler adds the retain in the setter because we specified it in the prop-erty. If we had set it to assign instead, no retain would have occurred. Likewise, the locking of the ivar is a default option that we could have 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 @synthesize keyword is smart about what it implements. If we need to implement our own setter for a property, it will not overwrite that setter.

Alongside the @synthesize property, we have the @dynamic property. Unlike

@synthesize, which generates the accessors for us, @dynamic tells the compiler that while the accessors for the property are not 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 situ-ation where we are implementing the accessors ourselves. If the accessor methods will be implemented at runtime, we would use the @dynamic property instead of the @synthesize property so that the compiler does not produce a warning. This is particularly useful for Core Data subclasses, discussed in Chapter 1, Under the Hood of Core Data, on page 1.

It should be noted that it is possible to have one @property definition that does not match the name of the ivar. For example, it is fairly common to have ivars that start with an underscore, but the accessors do not include the under-score. The @property can handle this as well as part of the @synthesize and

@dynamic calls.

@interface MyObject : NSObject {

NSString *_myString; //Only required for 32-bit }

@property (retain) NSString *myString;

@end

@implementation MyObject

@synthesize myString = _myString;

@end

Note that as of iOS 6.0 and Mac OS X 10.8, the ivar is different from the property name by default. Unless you override the default, the ivar name will have an underscore prefix.

Key Value Coding

141

In document Core Data 2nd Edition (Page 148-153)