Why rename synthesized properties in iOS with leading underscores? [duplicate]
Solution 1:
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 tonil
. Had you released the instance variable directly and not zeroed-out the reference, accessing the deallocated object would have caused a loudEXC_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. :)
Solution 2:
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
Solution 3:
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).