When overriding objective c-getters

Over the past year, I have worked with other people for the first time on some Objective-C projects.

Sometimes (and more often) I see other people overriding getter / accessor methods AND containing the implementation code in that method! This is a crazy city for me, since that is the whole point of having a setter ... it also means that the property set in the setter will simply be overridden in the getter, and therefore pointless.

Are these people behaving badly, or am I the one who is missing something? Is there a need to override a synthesized property getter?

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:

Width Ok is a bad example. Too many people fall into the semantics of "what variable" and "do not include entry into Objective-C accessors". So I updated the above example to ignore irrelevant semantics and focus on the concept. The concept is ... is there any example where you want to override GETTER (not just setter, getter). I am overriding setter many times, this question is about getter)?

Returning another property like a layer (as noted below) is a genuine example. But more specifically, ever need a SET property in a GETTER ? These are some of the oddities I see, so I updated the getter above to pull the value from NSUserDefaults to help my point ...

+3


source to share


5 answers


The first problem is that you don't want to use getWidth. A template in objC is name and setName. Don't use getName. This will mess up the binding and KVO.

Also, if it just sets / gets the iVar, there is no reason to override. If you are doing additional processing / validation then there might be an Ok override.



EDIT:

You should also try not to set data and do heavy processing in the getter. A getter must encapsulate some state and return data. It is expected to be very lightweight. Heavy processing and / or modifications must be done in methods or setters. For example, people set debug hours on getters and don't expect heavy processing and state changes.

+5


source


First, Cocoa's naming conventions would call getter -width

, not -getWidth

. "Get" is used to fill in the passed arguments:

- (void) getWidth:(CGFloat *)outWidth
{
    if (outWidth) *outWidth = _width;
}

      

However, back to your original question:

In the old days, before @property

and @synthesize

, we would have had to write our accessors by hand like you did above.

There are other times when you would like to manually write an accessory.

One common one is to defer initialization until a value is needed. For example, let's say there is an image that is required to be generated. Every time you change a property that would change the image, you don't want to immediately redraw the image. Instead, you can postpone the draw until the next time someone 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 option is to forward the accessor / mutator implementation to another class. For example, it UIView

forwards many of its properties to support 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]; }

      


Upgrade upgrade to:

In your update, it looks like the code in question is trying to either store the width value using NSUserDefaults, or it tries to allow users to specify a custom value to override all returned width values. If the latter, your example is fine (although I would limit this practice as it could cause confusion for beginners).

If the former, you want to load the value from NSUserDefaults once and store the new value back to NSUserDefaults in the installer. 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

      

_width ivar is used as cache. The truth is stored in NSUserDefaults.

Note. I am using NSUserDefaults in this example as you used it in yours. In practice, I prefer not to mix NSUserDefault with my accessories;)

+12


source


How about a case where you create your property object lazily? I use this template 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];
    }
}

      

+1


source


There are many reasons for overriding getter and setter methods, for example I override setter methods when creating custom UITableViewCell objects so I can set a property and then within that method the setter will automatically update the label or something inside the cell, instead of me I need to call update function after setting the property.

You may want to overwrite the recipient if you want to store information differently than retrieve it, an example would be a phone number object where you can save it as 5551234567 and it will automatically be retrieved as 555-123-4567 or something. I rarely override getter methods, but I override setter methods quite often.

0


source


This is a perfectly acceptable practice for object-oriented programming. However, you need to be aware of the side effects. For example, you shouldn't be doing something like network access in the setter method.

However, in the above code, since they don't do anything different from what synthesized methods do, there is no reason to include implementations. They just clutter up the code.

0


source







All Articles