In order for key-value coding to locate the accessor methods to use for invocations of valueForKey:
, setValue:forKey:
, mutableArrayValueForKey:
and mutableSetValueForKey:
you need to implement the key-value coding accessor methods.
Note: The accessor patterns in this section are written in the form -set<Key>:
or -<key>
. The <key>
text is a placeholder for the name of your property. Your implementation of the corresponding method should substitute the property name for <Key>
or <key>
respecting the case specified by key. For example, for the property name
, -set<Key>:
would expand to -setName:
, -<key>
would simply be -name
.
Commonly Used Accessor Patterns
Collection Accessor Patterns for To-Many Properties
The format for an accessor method that returns a property is -<key>
. The -<key>
method returns an object, scalar or a data structure. The alternate naming form -is<Key>
is supported for Boolean properties.
The example in Listing 1 shows the method declaration for the hidden
property using the typical convention, and Listing 2 shows the alternate format.
Listing 1 Accessor naming variations for a hidden property key
- (BOOL)hidden |
{ |
// implementation specific code |
return ...; |
} |
Listing 2 Alternate form accessor for a hidden property key
- (BOOL)isHidden |
{ |
// implementation specific code |
return ...; |
} |
In order for an attribute or to-one relationship property to support setValue:forKey:
an accessor in the form -set<Key>:
must be implemented. Listing 3 shows an example accessor method for the hidden
property key.
Listing 3 Accessor naming convention to support a hidden property key
- (void)setHidden:(BOOL)flag |
{ |
// implementation specific code |
return; |
} |
You must also implement a suitable means of representing a nil
value for an attribute. The key-value coding method setNilValueForKey:
method is called when you attempt to set an attribute to nil
. This provides the opportunity to provide appropriate default values for your application, or handle keys that don’t have corresponding accessors in the class.
The following example sets the hidden
attribute to NO
when an attempt is made to it to nil
. It creates an NSNumber
instance containing the boolean value and then uses setValue:forKey:
to set the new value. This maintains encapsulation of the model and ensures that any additional actions that should occur as a result of setting the value will actually occur. This is considered better practice than calling an accessor method or setting an instance variable directly.
- (void)setNilValueForKey:(NSString *)theKey |
{ |
if ([theKey isEqualToString:@"hidden"]) { |
[self setValue:[NSNumber numberWithBool:YES] forKey:@"hidden"]; |
} else |
[super setNilValueForKey:theKey]; |
} |
Although your application can implement accessor methods for to-many relationship properties using the -<key>
and -set<Key>:
accessor forms, you should typically only use those to create the collection object. For manipulating the contents of the collection it is best practice to implement the additional accessor methods referred to as the collection accessor methods. You then use the collection accessor methods, or a mutable collection proxy returned by mutableArrayValueForKey:
or mutableSetValueForKey:
.
Implementing the collection accessor methods, instead of, or in addition too, the basic getter for the relationship, can have many advantages:
Performance can be increased in the case of mutable to-many relationships, often significantly.
To-many relationships can be modeled with classes other than NSArray
or NSSet
by implementing the appropriate collection accessors. Implementing collection accessor methods for a property makes that property indistinguishable from an array or set when using key-value coding methods.
You can use the collection accessor methods directly to make modifications to the collection in a key-value observing compliant way. See Key-Value Observing Programming Guide for more information on key-value observing.
There are two variations of collection accessors: indexed accessors for ordered to-many relationships (typically represented by NSArray
) and unordered accessors for relationships that don’t require an order to the members (represented by NSSet
).
The indexed accessor methods define a mechanism for counting, retrieving, adding, and replacing objects in an ordered relationship. Typically this relationship is an instance of NSArray
or NSMutableArray
, however any object can implement these methods and be manipulated just as if it was an array. You are not restricted to simply implementing these methods, you can also invoke them as well to interact directly with objects in the relationship.
There are indexed accessors which return data from the collection (the getter variation) and mutable accessors that provide an interface for mutableArrayValueForKey:
to modify the collection.
In order to support read-only access to an ordered to-many relationship implement the following methods:
-countOf<Key>
. Required This is the analogous to the NSArray
primitive method count
.
-objectIn<Key>AtIndex:
or -<key>AtIndexes:
. One of these methods must be implemented. They correspond to the NSArray
methods objectAtIndex:
and objectsAtIndexes:
.
-get<Key>:range:
. Implementing this method is optional, but offers additional performance gains. This method corresponds to the NSArray
method getObjects:range:
.
An implementation of the -countOf<Key>
method simply returns the number of objects in the to-many relationship as an NSUInteger
. The code fragment in Listing 4 illustrates the -countOf<Key>
implementation for the to-many relationship property employees
.
Listing 4 Example -count<Key>
implementation
- (NSUInteger)countOfEmployees |
{ |
return [employees count]; |
} |
The -objectIn<Key>AtIndex:
method returns the object at the specified index in the to-many relationship. The -<key>AtIndexes:
accessor form returns an array of objects at the indexes specified by the NSIndexSet
parameter. Only one of these two methods must be implemented.
The code fragment in Listing 5 shows -objectIn<Key>AtIndex:
and -<key>AtIndexes:
implementations for a to-many relationship property employees
.
Listing 5 Example -objectIn<Key>AtIndex:
and -<key>AtIndexes:
implementations
- (id)objectInEmployeesAtIndex:(NSUInteger)index |
{ |
return [employees objectAtIndex:index]; |
} |
- (NSArray *)employeesAtIndexes:(NSIndexSet *)indexes |
{ |
return [employees objectsAtIndexes:indexes]; |
} |
If benchmarking indicates that performance improvements are required, you can also implement -get<Key>:range:
. Your implementation of this accessor should return in the buffer given as the first parameter the objects that fall within the range specified by the second parameter.
Listing 6 shows an example implementation of the -get<Key>:range:
accessor pattern for the to-many employee
property.
Listing 6 Example -get<Key>:range:
implementation
- (void)getEmployees:(Employee **)buffer range:(NSRange)inRange |
{ |
// Return the objects in the specified range in the provided |
// buffer. For example, if the employees were stored in an |
// underlying NSArray |
[employees getObjects:buffer range:inRange]; |
} |
Supporting a mutable to-many relationship with indexed accessors requires implementing additional methods. Implementing the mutable indexed accessors allow your application to interact with the indexed collection in an easy and efficient manner by using of the array proxy returned by mutableArrayValueForKey:
. In addition, by implementing these methods for a to-many relationship your class will be key-value observing compliant for that property (see Key-Value Observing Programming Guide).
Note: You are strongly advised to implement these mutable accessors rather than relying on a accessor that returns a mutable array directly. The mutable accessors are much more efficient when making changes to the data in the relationship.
In order to be key-value coding compliant for a mutable ordered to-many relationship you must implement the following methods:
-insertObject:in<Key>AtIndex:
or -insert<Key>:atIndexes:
. At least one of these methods must be implemented. These are analogous to the NSMutableArray
methods insertObject:atIndex:
and insertObjects:atIndexes:
.
-removeObjectFrom<Key>AtIndex:
or -remove<Key>AtIndexes:
. At least one of these methods must be implemented. These methods correspond to the NSMutableArray
methods removeObjectAtIndex:
and removeObjectsAtIndexes:
respectively.
-replaceObjectIn<Key>AtIndex:withObject:
or -replace<Key>AtIndexes:with<Key>:
. Optional. Implement if benchmarking indicates that performance is an issue.
The -insertObject:in<Key>AtIndex:
method is passed the object to insert, and an NSUInteger
that specifies the index where it should be inserted. The -insert<Key>:atIndexes:
method inserts an array of objects into the collection at the indices specified by the passed NSIndexSet
. You are only required to implement one of these two methods.
Listing 7 shows sample implementations of both insert accessors for the to-many employee
property.
Listing 7 Example -insertObject:in<Key>AtIndex:
and -removeObjectFrom<Key>AtIndex:
accessors
- (void)insertObject:(Employee *)employee |
inEmployeesAtIndex:(NSUInteger)index |
{ |
[employees insertObject:anObject atIndex:index]; |
return; |
} |
- (void)insertEmployees:(NSArray *)employeeArray |
atIndexes:(NSIndexSet *)indexes |
{ |
[employees insertObjects:employeeArray atIndexes:indexes]; |
return; |
} |
The -removeObjectFrom<Key>AtIndex:
method is passed an NSUInteger
value specifying the index of the object to be removed from the relationship. The -remove<Key>AtIndexes:
is passed an NSIndexSet
specifying the indexes of the objects to be removed from the relationship. Again, you are only required to implement one of these methods.
Listing 8 shows sample implementations of -removeObjectFrom<Key>AtIndex:
and -remove<Key>AtIndexes:
implementations for the to-many employee
property.
Listing 8 Example -removeObjectFrom<Key>AtIndex:
and -remove<Key>AtIndexes:
accessors
- (void)removeObjectFromEmployeesAtIndex:(NSUInteger)idx |
{ |
[employees removeObjectAtIndex:index]; |
} |
- (void)removeEmployeesAtIndexes:(NSIndexSet *)indexes |
{ |
[employees removeObjectsAtIndexes:indexes]; |
} |
If benchmarking indicates that performance improvements are required, you can also implement one or both of the optional replace accessors. Your implementation of either -replaceObjectIn<Key>AtIndex:withObject:
or -replace<Key>AtIndexes:with<Key>:
are called when an object is replaced in a collection, rather than doing a remove and then insert.
Listing 8 shows sample implementations of -replaceObjectIn<Key>AtIndex:withObject:
and -replace<Key>AtIndexes:with<Key>:
implementations for the to-many employee
property.
Listing 9 Example -replaceObjectIn<Key>AtIndex:withObject:
and -replace<Key>AtIndexes:with<Key>:
accessors
- (void)replaceObjectInEmployeesAtIndex:(NSUInteger)idx |
withObject:(id)anObject |
{ |
[employees replaceObjectAtIndex:index withObject:anObject]; |
} |
- (void)replaceEmployeesAtIndexes:(NSIndexSet *)indexes |
withEmployees:(NSArray *)employeeArray |
{ |
[employees replaceObjectsAtIndexes:indexes |
withObjects:employeeArray]; |
} |
The unordered accessor methods provide a mechanism for accessing and mutating objects in an unordered relationship. Typically this relationship is an instance of NSSet
or NSMutableSet
. However, by implementing these accessors, any class can by used to model the relationship and be manipulated using key-value coding just as if it was an instance of NSSet
.
The getter variations of the unordered accessor methods provide simple access to the relationship data. The methods return the number of objects in the collection, an enumerator to iterate over the collection objects, and a method to compare an object with the contents of the collection to see if it is already present.
Note: It’s rare to have to implement the getter variations of the unordered accessors. To-many unordered relationships are most often modeled using instance of NSSet or a subclass. In that case the key-value coding will, if it doesn’t find these accessor patterns for the property, directly access the set. Typically, you only implement these methods if you are using a custom collection class that needs to be accessed as if it was a set.
In order to support read-only access to an unordered to-many relationship you would implement the following methods:
-countOf<Key>
. Required. This method corresponds to the NSSet
method count
.
-enumeratorOf<Key>
. Required. Corresponds to the NSSet
method objectEnumerator
.
-memberOf<Key>:
. Required. This method is the equivalent of the NSet
method member:
Listing 10 shows simple implementations of the necessary getter accessors that simply pass the responsibilities to the transactions
property.
Listing 10 Example -countOf<Key>
, -enumeratorOf<Key>
, and -memberOf<Key>:
accessors
- (NSUInteger)countOfTransactions |
{ |
return [transactions count]; |
} |
- (NSEnumerator *)enumeratorOfTransactions |
{ |
return [transactions objectEnumerator]; |
} |
- (Transaction *)memberOfTransactions:(Transaction *)anObject |
{ |
return [transactions member:anObject]; |
} |
The -countOf<Key>
accessor implementation should simply return the number of items in the relationship. The -enumeratorOf<Key>
method implementation must return an NSEnumerator
instance that is used to iterate over the items in the relationship. See Enumerators: Traversing a Collectionâs Elements in Collections Programming Topics for Cocoa for more information about enumerators.
The -memberOf<Key>:
accessor must compare the object passed as a parameter with the contents of the collection and returns the matching object as a result, or nil
if no matching object is found. Your implementation of this method may use isEqual:
to compare the objects, or may compare objects in another manner. The object returned may be a different object than that tested for membership, but it should be the equivalent as far as content is concerned.
Supporting a mutable to-many relationship with indexed accessors requires implementing additional methods. Implementing the mutable indexed accessors for your application to interact with the indexed collection in an easy and efficient manner through the use of the array proxy returned by mutableArrayValueForKey:
. In addition, by implementing these methods for a to-many relationship your class will be key-value observing compliant for that property (see Key-Value Observing Programming Guide).
Note: You are strongly advised to implement these mutable accessors rather than relying on a accessor that returns a mutable array directly. The mutable accessors are much more efficient when making changes to the data in the relationship.
In order to be key-value coding complaint for a mutable unordered to-many relationship you must implement the following methods:
-add<Key>Object:
or -add<Key>:
. At least one of these methods must be implemented. These are analogous to the NSMutableSet
method addObject:
.
-remove<Key>Object:
or -remove<Key>:
. At least one of these methods must be implemented. These are analogous to the NSMutableSet
method removeObject:
.
-intersect<Key>:
. Optional. Implement if benchmarking indicates that performance is an issue. It performs the equivalent action of the NSSet method intersectSet:
.
The -add<Key>Object:
and -add<Key>:
implementations add a single item or a set of items to the relationship. You only required to implement one of the methods. When adding a set of items to the relationship you should ensure that an object that an equivalent object is not already present in the relationship. Listing 11 shows an example pass-through implementation for the transactions
property.
Listing 11 Example -add<Key>Object:
and -add<Key>:
accessors
- (void)addTransactionsObject:(Transaction *)anObject |
{ |
[transactions addObject:anObject]; |
} |
- (void)addTransactions:(NSSet *)manyObjects |
{ |
[transactions unionSet:manyObjects]; |
} |
Similarly, the -remove<Key>Object:
and -remove<Key>:
implementations remove a single item or a set of items from the relationship. Again, implementation of only one of the methods is required. Listing 12 shows an example pass-through implementation for the transactions
property.
Listing 12 Example -remove<Key>Object:
and -remove<Key>:
accessors
- (void)removeTransactionsObject:(Transaction *)anObject |
{ |
[transactions removeObject:anObject]; |
} |
- (void)removeTransactions:(NSSet *)manyObjects |
{ |
[transactions minusSet:manyObjects]; |
} |
If benchmarking indicates that performance improvements are required, you can also implement the -intersect<Key>:
or -set<Key>:
methods.
The implementation of -intersect<Key>:
should remove from the relationship all the objects that aren’t common to both sets. This is the equivalent of the NSMutableSet
method intersectSet:
.
Listing 13 Example -intersect<Key>:
and -set<Key>:
implementations
- (void)intersectTransactions:(NSSet *)otherObjects |
{ |
return [transactions intersectSet:otherObjects]; |
} |
© 2003, 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-02-04)