First, Cocoa naming conventions would call the getter -width
, not -getWidth
. "Get" is used to fill passed in arguments:
- (void) getWidth:(CGFloat *)outWidth
{
if (outWidth) *outWidth = _width;
}
That said, back to your original question:
In the old days, before @property
and @synthesize
, we would have to write our accessors manually as you did above.
There are other occasions where you would want to manually write an accessor, however.
One common one is to delay initialization until a value is needed. For example, say that there is an image which takes awhile to generate. Each time you modify a property that would alter the image, you don't want to redraw the image immediately. Instead, you could defer the draw until the next time somebody asks:
- (UIImage *) imageThatTakesAwhileToGenerate
{
if (!_imageThatTakesAwhileToGenerate) {
// set it here
}
return _imageThatTakesAwhileToGenerate;
}
- (void) setColorOfImage:(UIColor *)color
{
if (_color != color) {
[_color release];
_color = [color retain];
// Invalidate _imageThatTakesAwhileToGenerate, we will recreate it the next time that the accessor is called
[_imageThatTakesAwhileToGenerate release];
_imageThatTakesAwhileToGenerate = nil;
}
}
Another use is to forward the implementation of the accessor/mutator to another class. For example, UIView
forwards many of its properties to backing CALayer
:
// Not actual UIKit implementation, but similar:
- (CGRect) bounds { return [[self layer] bounds]; }
- (void) setBounds:(CGRect)bounds { [[self layer] setBounds:bounds]; }
- (void) setHidden:(BOOL)hidden { [[self layer] setHidden:hidden]; }
- (BOOL) isHidden { return [[self layer] isHidden]; }
- (void) setClipsToBounds:(BOOL)clipsToBounds { [[self layer] setMasksToBounds:clipsToBounds]; }
- (BOOL) clipsToBounds { return [[self layer] masksToBounds]; }
Update to asker's update:
In your update, it looks like the code in question is either trying to persist the value of width using NSUserDefaults, or it is trying to allow for users to specify a custom value to override all returned widths. If the latter, your example is fine (although I would limit this practice as it could cause confusion to newcomers).
If the former, you want to load the value from NSUserDefaults once, and save a new value back to NSUserDefaults in the setter. For example:
static NSString * const sUserWidthKey = @"USER_WIDTH";
@implementation Foo {
CGFloat _width;
BOOL _isWidthIvarTheSameAsTruthValue;
}
@synthesize width = _width;
- (CGFloat) width
{
if (!_isWidthIvarTheSameAsTruthValue) {
NSNumber *userWidth = [[NSUserDefaults standardUserDefaults] objectForKey:sUserWidthKey];
if (userWidth != nil) _width = [NSNumber doubleValue];
_isWidthIvarTheSameAsTruthValue = YES;
}
return _width;
}
- (void) setWidth:(CGFloat)newWidth
{
if (_width != newWidth) {
_width = newWidth;
NSNumber *userWidthNumber = [NSNumber numberWithDouble:_width];
[[NSUserDefaults standardUserDefaults] setObject:userWidthNumber forKey:sUserWidthKey];
_isWidthIvarTheSameAsTruthValue = YES;
}
}
@end
The _width ivar is being used as a cache. The truth is stored in NSUserDefaults.
Note: I'm using NSUserDefaults in this example since you used it in yours. In practice, I prefer to not mix NSUserDefault with my accessors ;)
When XCode auto-synthesizes, it simulates the following...
@synthesize foo = _foo;
So, you can access the data appropriately with self.foo or object.foo.
However, in the implementation of accessor methods (and initializers and dealloc), you should use the iVar directly.
Note, those iVars have two underscores. And, they are being manipulated in the accessor method. Use _managedObjectContext, and you should be good to go.
Best Answer
I had the same problem and after doing a bit of research, here is my conclusion about this issue:
The compiler warns you about a
@property
that you declared as atomic (i.e. by omitting thenonatomic
keyword), yet you provide an incomplete implementation of how to synchronize access to that property.To make that warning disappear:
If you declare a
@property
to be atomic then do one of the following:@dynamic
or;@synthesize
and keep the synthesized setter and getter or;If you declare the
@property
with(nonatomic)
then you can mix manual and synthesized implementations of getters and setters.Update: A Note on Property Auto-Synthesis
As of LLVM 4.0, CLang provides auto-synthesis for declared properties that are not
@dynamic
. By default, even if you leave out the@synthesize
, the compiler will provide getter and setter methods for you. However, the rule for atomic properties is still the same: Either let the compiler provide both the getter and the setter, OR implement them both yourself!