Reputation: 1070
Within the last year, I've been working with other people on some Objective-C projects for the first time.
Occasionally (and increasingly) I'm seeing other people overriding getter/accessor methods, AND containing implementation code in this method! To me this is crazy town, as this is the whole point of having a setter...it also means that the property being set in the setter will just be overridden in the getter, and therefor pointless.
Are these people behaving badly, or am I the one who's missing something? Is there EVER a need to override a synthesized property's getter method?
Example:
@synthesize width;
- (CGFloat)width {
NSNumber *userWidth = [[NSUserDefaults standardUserDefaults] objectForKey:@"USER_WIDTH"];
if (userWidth != nil)
{
width = [NSNumber floatValue];
}
//Shouldn't the above lines be done in the SETTER? I have SET the property!
//Why would I ever need to write anything BUT the below line??
return width;
}
- (void)setWidth:(CGFloat)newWidth {
//THIS is where you do the the setting!
width = newWidth;
}
UPDATE:
Ok width is a bad example. Too many people are getting caught up on the semantics of "what the variable is" and "don't include get in objective-c accessors". So I've updated the above example to ignore the irrelevant semantics, and concentrate on the concept. The concept being...is there any example when you'd want to override the GETTER (not setter, getter only. I override the setter many times, this question is about the getter)?
Returning another property such as layer (as mentioned below) is a genuine example. But more specifically is there ever a need to SET the property in a GETTER? This is some of the weirdness that I'm seeing, so i've updated the getter above to pull a value from the NSUserDefaults to aid my point...
Upvotes: 3
Views: 9720
Reputation: 39296
The first issue is you don't want to use getWidth. The pattern in objC is name and setName. Do not use getName. It messes up binding and KVO.
Also, if it's just setting/getting the iVar, there's no reason to override. If you're doing extra processing/validation then it may be Ok to override.
EDIT:
You should also try to avoid setting data and doing heavy processing in the getter. A getter is supposed to encapsulate some state and return data. The expectation is that it's very light weight. Heavy processing and/or modifications should be done in methods or setters. For example, folks set debug watches on getters and don't expect heavy processing and modification of state.
Upvotes: 5
Reputation: 5128
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 ;)
Upvotes: 12
Reputation: 45088
How about the case when you create your property object lazily? I use this pattern very often, also used in Xcode's CoreData template, etc.
- (NSString *)string
{
if (!_string) {
// Create the string property lazily
// Create is using some other internal, etc values
_string = [NSString alloc] initWith...]
}
return _string;
}
Also:
- (void)setString:(NSString *)string
{
if (![string isEqualToString:_string]) {
// Probably you want to make your property observable here too :)
[_setString release];
_setString = [string retain];
// Update other things that depend on _string for example redraw the view, etc
[self setNeedsDisplay];
}
}
Upvotes: 1
Reputation: 17317
This is a perfectly acceptable practice in object oriented programming. However, one needs to be aware of the side effects. You shouldn't do something like network access in a setter method, for example.
However, in the code you list above since they don't do anything different from what the synthesized methods do, there is no reason to include the implementations. They just clutter up the code.
Upvotes: 0
Reputation: 1254
There are plenty of reasons for override both getter and setter methods, for example I override setter methods when I make custom UITableViewCell objects, so that I can set a property, and then within that setter method, it will automatically update a label or something within the cell, instead of me having to call an update function after setting the property.
You may want to overwrite a getter if you want to store information differently than receiving it, an example might be a phone number object, where you might store it as 5551234567 and it will automatically be retrieved as 555-123-4567 or something like that. I rarely override getter methods myself, but I override setter methods quite frequently.
Upvotes: 0