Accessors
The Accessors pattern (also known as “setters and getters”) describes a technique for
funneling all access to an object’s properties through well-defined and easy to recognize
methods called accessors
The Accessors pattern maximizes implementation flexibility while minimizing opportunities for errors. The Accessors pattern provides the following benefits:
* Implementation flexibility. Properties can be stored as instance variables or using other techniques such as the Associative Storage pattern, and the implementation can be chaged without breaking other code.
* Minimum maintenance
* Simple usage of Cocoa's reference couted memory management conventions.
* Support Cocoa Key Value Coding and Key Value Observing technologies.
* Enable special processing when Outlets connected in Interface Builder are reestablished during .nib loading. ???
Motivation
Funnel access to an object's properties through methods to hide implementation details and confine code related to memory management to those methods. Cocoa's reference counted memory management provides a relatively simple pragmatic solution to the diffcult problem of memory management, and accessors play a pivotal role in keeping memory management simple.
Enable use of higher level Cocoa technologies like Bindings, KeyValue Coding, and key Value Observing with your custom classes.
Solution
-(float)interestRate
{
return interestRate;
}
Accessors that return values directly are named after the value returned. The interestRate value is returned by the -interestRate method. Cocoa uses the word get in the name of an accessor to indicate that a value will be returned indirectly by reference. The following implementation returns the interest rate by reference:
-(void)getInterestRate:(float *)aFloatPtr
{
if(NULL != aFloatPtr)
{
*aFloatPtr = interestRate;
}
}
There is seldom a reason to return values by reference, and accessors with the word get in their names are rare within Cocoa.
Accessors for setting nonobject properties are also straightforward.
-(void)setInterestRate:(float)aRate
{
interestRate = aRate;
}
Accesors for object properties are even more important because they provide an ideal place to centralize memory management in code.
Reference Counted Memory Management
When a Cocoa object is first allocated, it has an implicit reference count of one. If the reference count ever reaches zeros, the object is immediatedly deallocated. If an object needs to store a reference to another object, the reference count of the referenced object is increased by callng the -retain method declared in NSObject. When an object no longer needs a reference to another object, the reference count of the referenced objects is decreased by calling the -release method.
Accessors That Manage Retain Counts
When returning or setting object properties,
The simplest way to return an object value from an accessor is to return it directly. Given an object that stores a "title" property as an NSString instance variable named _myTitle, the title is returned by the -(NSString *)title method as follows:
- (NSString *)title
{
return _myTitle; //the properties names and instance variable names do not have to //correspond
}
In a multithreaded application, there is a chance that another thread of execution may alter _myTitle or release it after it has been returned from the -title method but before the code that called -title has had an opportunity to retain the returned object. In that case, _myTitle's retain count may reach zero, and the object may be deallocated, leaving the code that called -title with a pointer to a deallocated (invaid) object.
One solution that supports the multithreaded case is to retain and autorelease the object being returned as follows:
- (NSString *)title
{
id resultl
//lock
result = [[_myTitle retain] autorelease];
//unlock
return result;
}
The -autorelease method can be called in any situation that -release is called, but it's less efficient that -release because of extra logic and data structures needed to implement the delay. Retaining and autoreleasing the object to be returned assures that the object's reference count will not reach zero before the calling code has an opportunity to reatin it. ?
The lock object must be created before any calls to either accessor. like result in the upper code.
The most common implementation of a set accessor for an object property uses the pattern shown in the following implementation of the -(void)setTitle: (NSString *)aTitle method:
- (void)setTitle:(NSString *)aTitle
{
[aTitle retain];
[_myTitle release];
-myTitle = aTitle;
}
The aTitle argument will be stored as the new value of the _myTitle instance variable. The new object to be stored must be retained so that it isn't deallocated while it is still being used. The old object stored in the instance variable is released because it is no longer being used. If no other object has retained the old value, it is immediately deallocated when it is released. Finally, the _myTitle instance variable is assigned the new value.
Note
Objective-C objects are always stored and passed by reference. The _myTitle instance variable is actually a pointer to an object. The aTitle argument is a pointer to another object. When the statement _myTitle = aTitle is exectued, only a pointer is copied. The _myTitle instance variable is set to point to the same object as aTitle.
The cases the existing stored object property may be nil. The following describes the set Accessor's behavior in each case:
* The argument to the set Accessor is nil~~The retain message is harmlessly sent to nil. It's safe to send any message to nil as long as you don't count on any return value. The currently stored object is released. Finally, nil is stored as the new value of the object property.
* The argument references an object that isn't already stored--The new object is retained so that it will not be deallocated if it's not retained by any other objects. The old object is released and will be deallocated if it's not retained by any other objects. Finally, a pointer to the new object is stored as the value of the property being set.
* The argument references the same obejct that is already stored~~The referenced object already has a retain count of at least one because it is being used. The first thing the set Accessor does is retain the object causing its retain count to be no less then two. The object reference already stored is released, but because it is the same object that was just retained, its retain count drops to no less than one. Finally a harmless pointer assignment that sets the property to the same value already stored takes place.
The set Accessor pattern shown for the -setTitle: method must be implemented differently to address multithreading issues. The multithread safe version uses the following pattern:
- (void)setTitle:(NSString *)aTitle
{
id oldValue;
[aTitle retain];
//Lock
oldValue = _myTitle;
_myTitle = aTitle;
//unlock
[oldValue release];
}
Confining Memory Management to Accessors
If the Accessors pattern is consistently applied, almost all memory management for objects can be confined to accessors. When initializing object instances, use a set accessor to set the value of each object property.
The -dealloc method can indirectly release referenced objects using the set accessors and avoid memory management code in its implementation as follows:
- (void)dealloc
{
[self setStringValue:nil];//any previous string value is released.
[super dealloc];
}
Mutability
@interface MytitleStorage
{
NSMutableString *_myTitle;
}
@end
@implementation
- (id)init
{
self = [super init];
[self setTitle:@"Default Title"];
return self;
}
- (NSString *)title
{
return _myTitle; //This is safe because the type we claim to return
//is immutable and other programmers should respect
}
- (NSMutableString *)mutableTitle
{
//Return a copy of the instance varibale so that changes made
// to the copy do not affect the instance variable
return [[_myTitle mutableCopy] autorelease];
}
- (void)setTitle:(NSString *)aTitle
{
NSMutableString *newValue = [aTitle mutableCopy];
[_myTitle release];
_myTitle = newValue;
}
- (void)dealloc
{
[self setTitle:nil];
[super dealloc];
}
@end
Accessors that handle mutable object properties oftern use the -(id)mutableCopy method declared by the NSMutableCopying protocol to copy the object passed in or returned so that no reference to the mutable instance variable is avaiable outside the class where it's stored.
NSKeyValueCoding
Cocoa's NSKeyValueCoding inforaml protocol is defined as part of the Foundation framework. Informal protocols are explained in Chapter 6, "Category." In the simplest terms, informal protocols are groups of methods that you can assume are available for use even when you are dealing with an anonymous objec.
NSKeyValueCoding defines a mechanism for accessing an object's properties by name. The primary methods taht enable this access are
-setValue:(id)aValue forKey:(NSString *)aKey and
-(id)valueForKey:(NSString *)aKey,
which set and get named properties, respectively.
In all cases, the Key: argument is a reference to an NSString instance that stores the name of the property being accessed. The Value: argument of the -setValue:forKey: method requires a reference to an object as its argument, and -valueForKey: method requires a reference to an object as its argument, and -valueForKey: returns an object reference. Nonobject properties are set and returned by wrapping them in instances of Cocoa's NSValue class.
In some respects, the NSKeyValueCoding protocol provides an alternative to using accessor methods. However, accessor methods are such a good idea that even NSKeyValueCoding protocol methods operate with the help of accessor methods whenever possible. To set or return an object's properties, the NSKeyValueCoding methods use the following techniques in order:
1 Check for the existence of accessor methods named -<Key> or -get<Key> and use the mehods if possible to return a value. Check for the existence of a method named -set<Key>; and use it to set values. For both the -get<Key> and -set<Key>: methods, the first letter of the Key string is made uppercase to be consistent with Cocoa's method naming conventions.
2 If an accessor method based directlyon the key name is not available, check methods named -_<key>, -_get<key>, and -_set<key>:
3 If no accessor method is found, attempt to directly access an instance variable. The instance variable can be named either <key> or _<key>.
4 Finally,if the property cannot be accessed through accessor methods or directly through an instance variable, invoke one of the NSKeyValueCoding methods -handleQueryWithUnboundKey: or -handleTakeValue:forUnboundKey: as appropriate. The default implementation of these methods raise an exception, but you can override the methods to do something else.
The Cocoa's nib file loading code will use Accessor methods for setting object properties if you provide them. Or will set the properties directly.
Interface Builder Outlets pp118
Outlets are instance variables that can be connected to point to other objects within Apple's Interface Builder application.
Interface Builder saves all of the interconnected objects into files with the .xib extension by using the Archiving and Unarchiving pattern explained in Chapter 11.
As the objects are loaded (unarchived) into a running Cocoa application, the outlets are reconnected. If you provide properly named accessor methods for your object's outlets, your methods will be called to set the values of the outlets.Your accessor methods are free to perform additional processing as needed. If you don't provide a properly named set accessor emthod for each outlet, Cocoa's .nib loading code will set the outlet variable directly using Objective-C runtime functions.
Objective-C 2.0 @Synthesize directive
Examples in Cocoa
Cocoa classes provied accessors for all properties that can be examined or set outside the class where they are used.
Consequences
Using accessors is almost always the best solution for getting and setting an object's properties by code that is not within of the object's own implementation. In many cases, the accessors are used exclusively to access instance variables even within the implementation of a calss. Using accessors limits the number of places in code where explicit memory management is needed and provides many other benefits.
However, accessors require the overhead of at least one method call and usually several. It's always more efficient to access instance variables directly. Use accessors until it has been proven through profiling and other techniques that more performance is required. Then replace the use of accessors with direct use of variable only when necessary.
posted on 2013-02-23 19:08 Chansonyan 阅读(416) 评论(0) 收藏 举报
浙公网安备 33010602011771号