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).
You can use the Objective-C 2.0 declared 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) BOOL 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 |
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 (for ordered and unordered to-many relationships) follow the patterns described in detail in Key-Value Coding Programming Guide. To summarize, however; given a relationship named <key>
:
For an ordered relationship, 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 an unordered relationship, 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:idx]; |
} |
- (void)replaceObjectInEmployeesAtIndex:(NSUInteger)idx withObject:(id)anObject { |
    [employees replaceObjectAtIndex:idx withObject:anObject]; |
} |
Last updated: 2009-08-03