Why rename synthesized properties in iOS with leading underscores? [duplicate]
Asked Answered
X

4

126

Possible Duplicate:
How does an underscore in front of a variable in a cocoa objective-c class work?

When creating a new project in Xcode 4, the boilerplate code adds an underscore character when it synthesizes the ivars in the implementation file as:

@synthesize window = _window;

or:

@synthesize managedObjectContext = __managedObjectContext;

Can someone tell me what is being accomplished here? I'm not a complete nube, but this is one aspect of objective-C I don't understand.

Another point of confusion; in the app delegate implementation, after synthesizing the window iVar as above, in the application didFinishLaunchingWithOptions: method the window and viewController ivars are referred to using self:

self.window.rootViewController = self.viewController
[self.window makeKeyAndVisible];

but in the dealloc method it's _window, or _viewController

Thanks

Xylophone answered 29/3, 2011 at 0:11 Comment(0)
B
223

This is an artifact of a previous version of the Objective-C runtime.

Originally, @synthesize was used to create accessors methods, but the runtime still required that instance variables had to be instantiated explicitly:

@interface Foo : Bar {
  Baz *_qux;
}

@property (retain) Baz *qux;
@end

@implementation Foo
@synthesize qux = _qux;

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

@end

People would prefix their instance variables to differentiate them from their properties (even though Apple doesn't want you to use underscores, but that's a different matter). You synthesize the property to point at the instance variable. But the point is, _qux is an instance variable and self.qux (or [self qux]) is the message qux sent to object self.

We use the instance variable directly in -dealloc; using the accessor method instead would look like this (though I don't recommend it, for reasons I'll explain shortly):

- (void)dealloc {
  self.qux = nil; // [self setQux:nil];
  [super dealloc];
}

This has the effect of releasing qux, as well as zeroing out the reference. But this can have unfortunate side-effects:

  • You may end up firing some unexpected notifications. Other objects may be observing changes to qux, which are recorded when an accessor method is used to change it.
  • (Not everyone agrees on this point:) Zeroing out the pointer as the accessor does may hide logic errors in your program. If you are ever accessing an instance variable of an object after the object has been deallocated, you are doing something seriously wrong. Because of Objective-C's nil-messaging semantics, however, you'll never know, having used the accessor to set to nil. Had you released the instance variable directly and not zeroed-out the reference, accessing the deallocated object would have caused a loud EXC_BAD_ACCESS.

Later versions of the runtime added the ability to synthesize instance variables in addition to the accessor methods. With these versions of the runtime, the code above can be written omitting the instance variables:

@interface Foo : Bar
@property (retain) Baz *qux;
@end

@implementation Foo
@synthesize qux = _qux;

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

@end

This actually synthesizes an instance variable on Foo called _qux, which is accessed by getter and setter messages -qux and -setQux:.

I recommend against this: it's a little messy, but there's one good reason to use the underscore; namely, to protect against accidentally direct ivar access. If you think you can trust yourself to remember whether you're using a raw instance variable or an accessor method, just do it like this instead:

@interface Foo : Bar
@property (retain) Baz *qux;
@end

@implementation Foo
@synthesize qux;

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

@end

Then, when you want to access the instance variable directly, just say qux (which translates to self->qux in C syntax for accessing a member from a pointer). When you want to use accessors methods (which will notify observers, and do other interesting things, and make things safer and easier with respect to memory management), use self.qux ([self qux]) and self.qux = blah; ([self setQux:blah]).

The sad thing here is that Apple's sample code and template code sucks. Never use it as a guide to proper Objective-C style, and certainly never use it as a guide to proper software architecture. :)

Brycebryn answered 29/3, 2011 at 0:29 Comment(8)
There is a very good reason for @synthesize quz = _quz;; it eliminates accidentally writing quz when you mean self.quz and vice-versa. The compiler issue was relatively short lived, but real. If you find examples that are borked, please file bugs.Cythera
@Cythera Good point re underscore-naming. I usually trust myself to type the right thing (or at least fix it if I mess up), but it's definitely something to think about when crafting your coding-style (I tend to err on the side of aesthetics, but it's perfectly valid to lean towards protection against accidents).Brycebryn
If you don't want to write all the boilerplate by hand you might want to use github.com/holtwick/xobjc (shameless self promo of an open source project ;) ) It supports the leading and the trailing underscores. The trailing ones are recommended by the Google Styleguide for ObjC. This is also useful, because Apple uses leading underscores in their own code too and so you might get in trouble with private API in rare cases.Tarrant
I just use @synthesize quz = quz_, so I get the benefits of saving myself from mistypes, without treading on Apple’s suggestion for avoiding underscore prefixes. I also tend to use Jeff LaMarche’s MCRelease macro, as detailed here: iPhone Development: Dealloc, for all the reasons he states.Handful
@Andy That seems like a reasonable compromise!Brycebryn
why is it that the "__managedObjectContext" have two underscores in front of it? What's the rationale behind that?Unsuspecting
Where do you get "Apple doesn't want you to use underscores?". It's only for methods that they reserve underscore prefixes, not for variables.Bine
I'd love to see this answer updated with suggestions / ramifications for ARC-enabled projects.Funky
A
13

Here is another reason. Without underscoring instance variables you frequently obtain warning with the parameters self.title = title and self.rating = rating:

@implementation ScaryBugData
@synthesize title;
@synthesize rating;
- (id)initWithTitle:(NSString *)title rating:(float)rating {
    if (self = [super init]) {
        self.title = title; // Warning. Local declaration hides instance variable
        self.rating = rating; // Warning. Local declaration hides instance variable
    }
    return self;
}
@end

You avoid warning by underscoring instance variables:

@implementation ScaryBugData
    @synthesize title = _title;
    @synthesize rating = _rating;
    - (id)initWithTitle:(NSString *)title rating:(float)rating {
        if (self = [super init]) {
            self.title = title; // No warning
            self.rating = rating; // No warning
        }
        return self;
    }
    @end
Academician answered 7/5, 2012 at 15:17 Comment(1)
Got to love sample code from Ray Wenderlich's great Scary Bugs app tutorials :)Millipede
B
6

in the application didFinishLaunchingWithOptions: method the window and viewController ivars are referred to using self

No, they're not. Those are references to the properties window and viewController. That's the point of the underscore, to make it clearer when the property is being used (no underscore) and when the ivar is being accessed directly (with underscore).

Bullring answered 29/3, 2011 at 0:25 Comment(0)
O
2

Yes, Its is just to differentiate the reference of object. That is , if the object is referred directly use it with underscore, otherwise use self to refer the object.

Olives answered 30/11, 2011 at 4:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.