Objective-C: @property gotcha #1

Sometimes “best” practices can bite you.

In Apple iOS documentation, they strongly recommend the use of declared properties to automatically generate access methods to class variables.  In header files, you would have something like this:

@interface MyClass
{
    IBOutlet UIView* view;
}

@property (nonatomic, retain) IBOutlet UIView* view;

@end

 

Technically, this generates access methods named ‘view’ and ‘setView’, but in practice one generally accesses the class variable with the dot notation (i.e., ‘self.view’) .  The implementation of this class (minus any explicit methods) would be:

@implementation MyClass

@synthesize view;

@end

 

Accessing the variable with dot notation calls the access methods (as does calling them explicitly, obviously), but simply using ‘view’ (without the ‘self’ reference) accesses the variable directly, omitting any other functionality associated with the accessors, such as (in the above example) retaining the new view.  This is generally a poor practice except in initialization and deallocation routines, where it is preferred.

So, in order to assure that one does not accidentally skip the accessors, the recommended best practice is to use the ability of @synthesize to generate accessors that access a slightly different variable name.  In other words, instead of ‘view’ and ‘setView’ referencing the ‘view’ variable, they can be instead made to access ‘view_’:

@synthesize view = view_;

 

This is, in theory, a good idea because one can only access the variable directly with the explicit addition of an underscore (in this case), and simply referencing ‘view’ instead of ‘self.view’ should produce a compiler error.  (Note that we use a trailing underscore because leading underscores already have a specific meaning in our coding guidelines.)

Problem: If you modify the class implementation as noted above to put this into practice, you introduce a bigger problem into your code.  It turns out that @synthesize actually creates a class variable if none of the specified name exists, but it does not generate a warning or error if the one of the ostensible name does exist.  In other words, the above line would create a new ‘view_’ class variable, and create accessor methods, despite the fact that ‘view’ already exists.  The ‘view’ and ‘setView’ methods modify ‘view_’, so ‘self.view’ does the same, but failing to ‘self’ reference, rather than generating the intended error, actually modifies the orphaned ‘view’ variable.  Instead of having an extra compiler check, you instead have two very similar variables in the same class and any error becomes even harder to diagnose.

If the @property/@synthesize combination essentially replaces normal variable declarations, it obviates the whole issue of these declarations.  This seems rather silly/weird, since declaring [class] variables is a staple of C[++], and the method of doing this in Objective-C is one of the first lessons taught.  Nevertheless…

Solution: To make this idea an actual best practice, at least for simple classes, omit the entire parenthesized section of the class declaration and just declare all variables via @property/@synthesize statements.  You would end up with something like the following:

@interface MyClass
@property (nonatomic, retain) IBOutlet UIView* view;
@end
@implementation MyClass
@synthesize view = view_;

- (id)init
{
    self = [super init];
    if ( self )
    {
        view_ = nil;
    }
    return self;
}

- (void)dealloc
{
    [view_ release];
    [super dealloc];
}

@end

 

All (or most) other references to the class variable would be via dot notation, ‘self.view’, which would (of course) modify the ‘view_’ variable via accessors, as desired.

Happy coding!

Leave a Reply

Your email address will not be published.