There are a number of ways you can ensure that your model objects are key-value coding (KVC) and key-value observing (KVO) compliant, typically and most easily by implementing suitable accessor methods as described in “Basic Accessor Methods.” The main exception to this rule is Core Data, which imposes special constraints on the implementation of accessor methods. If you are using Core Data, you should read Managed Object Accessor Methods to learn how to ensure your managed object classes are KVC and KVO compliant.
KVC Compliance
KVO Compliance
Dependent Values
The key-value coding mechanism tries hard to find a value for a given key, so that it is actually difficult not to be KVC compliant for a given property. Although there are a number of ways to ensure compliance, it is recommended that you use accessor methods and follow standard naming conventions. The general requirements for KVC compliance are described in Key-Value Coding Accessor Methods in Key-Value Coding Programming Guide.
There are two ways you can implement KVO compliance—using automatic notification, or, using manual notification. As the name implies, if you use automatic notification you don't have to do anything other than implement (and use) standard accessor methods as described in “Basic Accessor Methods.” For most classes, automatic KVO notification is enabled by default, and there is typically no benefit in disabling automatic notification. The primary exception is in a subclass of NSManagedObject
.
Important: NSManagedObject
disables automatic notification by default. Moreover, you cannot enable automatic notification for modeled properties. If you implement accessors for model properties, you must invoke the relevant change notification methods. You can, however, enable automatic notification for unmodeled properties using automaticallyNotifiesObserversForKey:
.
If you want to disable automatic notification, you implement automaticallyNotifiesObserversForKey:
and return NO
for the keys for which you want to provide manual notifications. In your set accessors, for simple attributes you then invoke willChangeValueForKey:
and didChangeValueForKey:
respectively before and after the property key is changed. For a to-many relationship, you also need to invoke the relevant notification methods indicating the type of change and the indexes of the objects changed.
You can implement "bulk" modifiers that allow you to, for example, make a large number of additions to an array in a single method call rather than requiring an individual method call for each insertion. The following example shows a custom bulk modifier method for an ordered to-many relationship.
- (void)addObjectsToEmployeesFromArray:(NSArray *)otherArray |
{ |
if ([otherArray count] > 0) |
{ |
NSRange range = NSMakeRange([employees count], [otherArray count]); |
NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:range]; |
[self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes |
forKey:@"employees"]; |
[employees addObjectsFromArray:otherArray]; |
[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes |
forKey:@"employees"]; |
} |
} |
There are many situations in which the value of one property depends on that of one or more other properties. If the value of one attribute changes, then the value of the derived property should also be flagged for change. How you ensure that key-value observing notifications are posted for these dependent properties depends on which version of Mac OS X you’re using.
If you are targeting Mac OS X v10.5 and later, to trigger notifications automatically you should either override keyPathsForValuesAffectingValueForKey:
or implement a suitable method that follows the pattern it defines for registering dependent keys.
For example, you could override keyPathsForValuesAffectingValueForKey:
as shown in the following example:
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key |
{ |
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; |
if ([key isEqualToString:@"fullNameAndID"]) |
{ |
NSSet *affectingKeys = [NSSet setWithObjects:@"lastName", @"firstName", @"employeeID", nil]; |
keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKeys]; |
} |
return keyPaths; |
} |
Or, to achieve the same result, you could just implement keyPathsForValuesAffectingFullNameAndID
as illustrated in the following example:
+ (NSSet *)keyPathsForValuesAffectingFullNameAndID |
{ |
return [NSSet setWithObjects:@"lastName", @"firstName", @"employeeID", nil]; |
} |
Important: Note that you cannot set up dependencies on to-many relationships. For example, suppose you have an Order object with a to-many relationship (orderItems
) to a collection of OrderItem objects, and OrderItem objects have a price
attribute. You might want the Order object have a totalPrice
attribute that is dependent upon the prices of all the OrderItem objects in the relationship. You can not do this by implementing keyPathsForValuesAffectingValueForKey:
and returning orderItems.price
as the keypath for totalPrice
. You must observe the price
attribute of each of the OrderItem objects in the orderItems
collection and respond to changes in their values by updating totalPrice
yourself.
If you are targeting Mac OS X v10.3 and later, you should use setKeys:triggerChangeNotificationsForDependentKey:
to trigger notifications automatically. You set up the dependencies as illustrated in the following example:
+ (void)initialize |
{ |
NSArray *keys = [NSArray arrayWithObjects: |
@"firstName", @"lastName", nil]; |
[self setKeys:keys triggerChangeNotificationsForDependentKey: |
@"fullName"]; |
} |
Important: Note that you cannot set up dependencies on key paths. For example, suppose you have an Order object with a to-many relationship (orderItems
) to a collection of OrderItem objects, and OrderItem objects have a price
attribute. You might want the Order object have a totalPrice
attribute that is dependent upon the prices of all the OrderItem objects in the relationship. You can not do this with setKeys:triggerChangeNotificationsForDependentKey:
passing orderItems.price
as the key. You must observe the price
attribute of each of the OrderItem objects in the orderItems
collection and respond to changes in their values by updating totalPrice
yourself.
© 2008 Apple Inc. All Rights Reserved. (Last updated: 2008-02-08)