Ios – When to override objective c getters

getter-setteriosobjective cpropertiesxcode

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…

Best Answer

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 ;)