There are two basic forms of accessor, a "get accessor", and a "set accessor". You use the get accessor to retrieve a property value from an object; you use the set accessor to set a property value in an object. You can use the Objective-C 2.0 Properties feature to declare and implement accessor methods.
The implementation of an accessor method depends on the type of the property to which it provides access—that is, whether the property is an attribute or a relationship, and if it is a relationship whether it is a to-one or a to-many relationship—see Cocoa Design Patterns for more details. It also depends on whether you are using garbage collection.
Important: The accessor methods shown here are not thread-safe in a managed memory environment, where thread-safety requires the use of a lock which incurs considerable overhead. Typically you cannot express thread-safety at the level of an individual accessor method (see Threading Programming Guide).
Objective-C Properties
Attributes
Relationships
You can use the Objective-C 2.0 properties feature to avoid the need to write accessor methods yourself. In your class interface, you declare a specification for the properties using @property
:
@interface MyClass : NSObject |
{ |
NSString *myString; |
BOOL valid; |
} |
@property (copy, nonatomic) NSString *myString; |
@property (nonatomic, getter=isValid) valid; |
@end |
In the implementation, you use @synthesize
to direct the compiler to generate accessor methods corresponding to the property specification:
@implementation MyClass |
@synthesize myString; |
@synthesize valid; |
@end |
For full details, see Properties.
In most cases, this should be all you need. Sometimes, however, you may need to implement your own accessor methods—for example, for relationships you may want to make a mutable copy of a new value in a setter method. Even if you do implement custom accessors, you are encouraged to declare properties since they make your intent explicit.
Attributes are defining characteristics of a model object.
For performance reasons, the get accessor typically simply returns the value.
- (NSString *)firstName |
{ |
return firstName; |
} |
In the set accessor, you should typically make a copy of the new value that is then private to the model object, as shown in this example:
- (void)setFirstName:(NSString *)aFirstName |
{ |
if (firstName != aFirstName) |
{ |
[firstName release]; // omit if you only support a garbage-collected environment |
firstName = [aFirstName copy]; |
} |
} |
Note that this requires the attribute to implement the NSCopying
protocol. Most of the basic Cocoa classes you might use as an attribute implement NSCopying
. If you implement a custom class to represent an attribute, it is typically easy to also implement the copy method. In cases where a class is immutable, this might simply retain self
.
The following examples illustrate accessor methods for non-object attribute types.
- (NSRect)bounds |
{ |
return bounds; |
} |
- (void)setBounds:(NSRect)newBounds |
{ |
bounds = newBounds; |
} |
You typically also choose a suitable means of representing a nil
value using the given attribute type. To properly integrate with key-value coding (see “Key-Value Technology Compliance”) you should implement setNilValueForKey:
as illustrated in the following example.
- (void)setNilValueForKey:(NSString *)key |
{ |
if ([key isEqualToString:@"bounds"] |
{ |
bounds = NSMakeRect(0,0,0,0); |
} |
else |
{ |
[super setNilValueForKey:key]; |
} |
} |
The semantics of accessor methods for a relationship depend on whether the relationship is a to-one or a to-many relationship. In a to-one relationship, you maintain a reference to a related object; in a to-many relationship you need a private collection that maintains references to related objects. In addition, there are special accessors for to-many relationships that may make access more efficient and that allow you to represent the relationship using something other than a collection object.
In contrast to an attribute, a relationship is not a private characteristic of an object. As with the attribute, the get accessor typically simply returns the value.
- (Department *)department |
{ |
return department; |
} |
The set accessor does not copy a new value. If you use a managed memory environment, or if you need to support both managed memory and garbage collection, you release the old value and retain the new (retain
and release
are no-ops in a garbage-collected environment):
- (void)setDepartment:(Department *)newDepartment |
{ |
if (newDepartment != department) |
{ |
[department release]; |
department = [newDepartment retain]; |
} |
} |
If you use garbage collection, you can simply assign the new value:
- (void)setDepartment:(Department *)newDepartment |
{ |
department = newDepartment; |
} |
There are two forms of accessor for to-many relationships—the simple get and set form that follows the same pattern as attributes and to-one relationships, and the collection form. The latter is primarily used for integration with key-value coding and key-value observing.
- (NSArray *)employees |
{ |
return employees; |
} |
- (void)setEmployees:(NSMutableArray *)newEmployees |
{ |
if (employees != newEmployees) |
{ |
[employees autorelease]; |
employees = [newEmployees mutableCopy]; |
} |
} |
Collection accessors follow patterns, different for sets and arrays. The patterns are described in Key-Value Coding Programming Guide, but here is a summary. Given a relationship named <key>
:
For an array, you implement countOf<Key>
and objectIn<Key>AtIndex:
. You may also implement get<Key>:range:
. If you want to support mutations, you also implement insertObject:in<Key>AtIndex:
and removeObjectFrom<Key>AtIndex:
. Again to improve performance, you may also implement replaceObjectIn<Key>AtIndex:withObject:
.
For a set, you implement an add<Key>Object:
and remove<Key>Object:
pair, an add<Key>:
and remove<Key>:
pair, or both pairs. For greater efficiency, you can also implement intersect<Key>:
.
The following example illustrates collection accessors for an array; the analogous methods for sets are illustrated in Managed Object Accessor Methods.
- (NSUInteger)countOfEmployees |
{ |
return [employees count]; |
} |
- (id)objectInEmployeesAtIndex:(NSUInteger)idx |
{ |
return [employees objectAtIndex:idx]; |
} |
- (void)insertObject:(id)anObject inEmployeesAtIndex:(NSUInteger)idx |
{ |
[employees insertObject:anObject atIndex:idx]; |
} |
- (void)removeObjectFromEmployeesAtIndex:(NSUInteger)idx |
{ |
[employees removeObjectAtIndex:index]; |
} |
- (void)replaceObjectInEmployeesAtIndex:(NSUInteger)idx withObject:(id)anObject |
{ |
[employees replaceObjectAtIndex:idx withObject:anObject]; |
} |
© 2008 Apple Inc. All Rights Reserved. (Last updated: 2008-02-08)