There are two techniques for making a class’s properties observable using key-value observing. Automatic observing is provided by NSObject and is available for all properties of a class that are key-value coding compliant. Manual observing provides additional control over when observations are noted, and requires additional coding.
NSObject provides a basic implementation of automatic key-value observing. Using automatic observer notifications, it is not necessary to bracket changes to a property with invocations of willChangeValueForKey:
and didChangeValueForKey:
when mutating properties via key-value coding and key-value coding compliant methods. Automatic observer notification is controlled by the class method automaticallyNotifiesObserversForKey:
. The default implementation returns YES
for all keys.
Automatic key-value observing informs observers of changes made using key-value compliant accessors, as well as the key-value coding methods. The examples shown in Listing 1 result in any observers of the property name
to be notified of the change.
Listing 1 Methods of invoking key-value observing
// calling the accessor method |
[self setName:@"Savings"]; |
// using setValue:forKey: |
[self setValue:@"Savings" forKey:@"name"]; |
// using a key path, where account is a kvc-compliant property |
// of "document" |
[document setValue:@"Savings" forKeyPath:@"account.name"] |
Automatic notification is also supported for the collection proxy objects returned by mutableArrayValueForKey:
and mutableSetValueForKey:
. This works for to-many relationships that support the indexed accessor methods insertObject:in<Key>AtIndex:
, replaceObjectIn<Key>AtIndex:
, and removeObjectFrom<Key>AtIndex:
.
You can control automatic observer notifications for properties of your subclass by implementing the class method automaticallyNotifiesObserversForKey:
. Subclasses can test the key passed as the parameter and return YES
if automatic notification should be enabled, NO
if it should be disabled.
Manual key-value observer notification provides more granular control over how and when notifications are sent to observers. This can be useful to help minimize triggering notifications that are unnecessary, or to group a number of changes into a single notification.
A class that implements manual observer notification must override the NSObject implementation of automaticallyNotifiesObserversForKey:
. It is possible to use both automatic and manual observer notifications in the same class. For properties that perform manual observer notification, the subclass implementation of automaticallyNotifiesObserversForKey:
should return NO
. A subclass implementation should invoke super
for any unrecognized keys. The example in Listing 2 enables manual notification for the openingBalance
property allowing the superclass to determine the notification for all other keys.
Listing 2 Example implementation of automaticallyNotifiesObserversForKey:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey { |
BOOL automatic = NO; |
if ([theKey isEqualToString:@"openingBalance"]) { |
automatic=NO; |
} else { |
automatic=[super automaticallyNotifiesObserversForKey:theKey]; |
} |
return automatic; |
} |
To implement manual observer notification, you must invoke willChangeValueForKey:
before changing the value, and didChangeValueForKey:
after changing the value. The example in Listing 3 implements manual observer notifications for the openingBalance
property.
Listing 3 Example accessor method implementing manual observer notification
- (void)setOpeningBalance:(double)theBalance { |
[self willChangeValueForKey:@"openingBalance"]; |
openingBalance=theBalance; |
[self didChangeValueForKey:@"openingBalance"]; |
} |
You can minimize sending unnecessary notifications by first checking if the value has changed. The example in Listing 4 tests the value of openingBalance
and only provides the notification if it has changed.
Listing 4 Testing the value for change before providing notification
- (void)setOpeningBalance:(double)theBalance { |
if (theBalance != openingBalance) { |
[self willChangeValueForKey:@"openingBalance"]; |
openingBalance=theBalance; |
[self didChangeValueForKey:@"openingBalance"]; |
} |
} |
If a single operation causes multiple keys to change you must nest the change notifications as shown in Listing 5.
Listing 5 Nesting change notifications for multiple keys
- (void)setOpeningBalance:(double)theBalance { |
[self willChangeValueForKey:@"openingBalance"]; |
[self willChangeValueForKey:@"itemChanged"]; |
openingBalance=theBalance; |
itemChanged=itemChanged+1; |
[self didChangeValueForKey:@"itemChanged"]; |
[self didChangeValueForKey:@"openingBalance"]; |
} |
In the case of a to-many relationship, you must specify not only the key that changed, but also the type of change and the indexes of the objects involved. The type of change is an NSKeyValueChange
that specifies NSKeyValueChangeInsertion
, NSKeyValueChangeRemoval
, or NSKeyValueChangeReplacement
. The indexes of the affected objects are passed as an NSIndexSet.
The code fragment in Listing 6 demonstrates how to wrap a deletion of objects in the to-many relationship transactions
.
Listing 6 Implementation of manual observer notification in a to-many relationship
- (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes { |
[self willChange:NSKeyValueChangeRemoval |
valuesAtIndexes:indexes forKey:@"transactions"]; |
// remove the transaction objects at the specified indexes here |
[self didChange:NSKeyValueChangeRemoval |
valuesAtIndexes:indexes forKey:@"transactions"]; |
} |
Note: Care should be taken that you do not release the values that will change, before sending a willChange
message.
Last updated: 2009-08-14