10

Why does one want this underscore prefix in an iOS app?

Jawwad
  • 1,326
  • 2
  • 9
  • 18
Surfer
  • 109
  • 2

4 Answers4

3

Sometimes it helps to see whats happening behind the scenes to understand. Basically when you see this

@property (nonatomic, retain) Foo *foo;

With this in the implementation

@synthesize foo=_foo;

It's syntax sugar for the following, meaning that the compiler basically generates this code for you

Foo *foo = nil;

-(Foo *) foo {
    return _foo;
}

-(void) setFoo:(Foo *)val {
    if( _foo != val ) {
        [_foo release];
        _foo = [val retain];
    }
}

This way when you refer to the public property, you hit it through the generated accessories of self.foo and if you want to refer to the instance variable inside your class you can refer to _foo. Before XCode four it was easy to get confused between the two. You could do something like

self.foo = foo1;

foo = foo2;

This was perfectly legal and could cause problems on a couple levels. The second line doesn't use the accessor so foo2 would not be retained which could cause it to be picked up by garbage collection prematurely. Even worse, the second line doesn't use the accessor which would release any previous value meaning it would cause a memory leak.

So in summary, the new technique creates a getter and setter for you property and allows you to also specify the name to be used for the instance variable, used for encapsulation.

jerrylroberts
  • 3,419
  • 23
  • 22
  • Good explanation. Just nitpicking: `if( _foo != nil ) [_foo release];` the compiler is not generating a test vs nil, which would be useless, it would rather test if new value is different from previous value. See http://stackoverflow.com/questions/3924463/how-is-retain-setter-implemented-with-synthesize/3925204#3925204 for an example. – jv42 Dec 19 '11 at 14:38
  • Try running your code with `Foo *tmp = [Foo foo]; self.foo = tmp; self.foo = tmp;` to see it go boom. – jv42 Dec 19 '11 at 14:43
  • I banged that answer out on my iPad, I wasn't 100% sure of the exact implementation but I wanted to really highlight the fact that it handles memory management for you and helps avoid collisions. Thanks for the heads up! – jerrylroberts Dec 19 '11 at 16:41
  • jv42, on your second comment, Foo *tmp = [Foo foo]; shouldn't compile because -(Foo *)foo is not accessible at a class level. Now if the signature were changed to +(Foo *)foo then [Foo foo] would be perfectly legal, however, that is not the case. – jerrylroberts Dec 19 '11 at 17:08
  • Mmmm, sorry I was implying a method using standard naming convention returning an autoreleased instance of Foo. Here the property name is not conforming to this convention. – jv42 Dec 19 '11 at 17:23
1

Here's why I do this at times:

If I want to do lazy loading on a property I will typically underscore my ivar so that the work done to load the desired data from the back-end server/saved file/what-have-you is only loaded the first time. For example:

- (NSMutableArray *)recentHistory;
{
   if (_recentHistory == nil)
   {
       // Get it and set it, otherwise no work is needed... hooray!
   }

   return _recentHistory;
}

So here calls to the property [instanceOfClass recentHistory] (or instanceOfClass.recentHistory) will check the ivar to see if it needs to load the data or just return the already loaded data.

It is overkill to declare all of your properties this way.

Hope that helps.

Ryan Crews
  • 3,015
  • 1
  • 32
  • 28
  • 2
    But do you really have to use underscore for this or do you mean just as a convention? you could just have `@synthesize recentHistory;` and use the instance variable `recentHistory` or `self->recentHistory` inside the `recentHistory` method, just make sure not to use the property name `self.recentHistory` or `[self recentHistory]` and cause a recursion. – Mattias Wadman Dec 19 '11 at 15:10
  • no no, as a convention. It's a convention you will find in Apple's code as well, which is likely why people use it. The idea of having an iVar and a property is sometimes overused, but this is a case where I think it makes since. – Ryan Crews Jan 06 '12 at 06:48
1

just wanted to add my thoughts to all the above answers.

in my case, i use it mainly to keep my classes safe from accidents.

in my .h files, i declare only the properties with no ivars. in the .m file, i use the above @synthesize pattern to hide the actual ivar from users, including myself, to be forced to use the synthesized/dynamic accessors and not the otherwise ivars directly. you could use anything for your ivar name and not just underscore it. e.g. you could do:

@synthesize foo = _mySecretFoo;
@synthesize bar = oneUglyVarBecauseIHateMyBoss;

this way, your boss would only see bar, and you would find it much easier, and thus safer, to use the bar accessor - whether you use dot notation or messages.

i prefer this approach over the other,

@property (getter=foo, setter=setFoo, ...) _mySecretFoo;
@property (getter=bar, setter=setBar, ...) oneUglyVarBecauseIHateMyBoss;

as this does not enforce private implementation and encapsulation, and it's just extra typing when Xcode can do the same for you. one thing to remember, properties are not the same as ivars! you could have more properties than ivars, or the other way around.

Curtis
  • 101,612
  • 66
  • 270
  • 352
Salam Horani
  • 101
  • 2
  • 9
  • If I've read this right, you do this without explicitly declaring ivars in an `@implementation {...}` block. What is the advantage of using `@synthesize bar = oneUgly;` if you never get to refer to `oneUgly`? Why not just do `@synthesize bar;`? Is it because your way gives errors if you write eg `bar = 123.0`? – Tim Dec 19 '11 at 15:04
  • you mean declaring ivars in an @interface {} block. yes you would get an error if you use bar = 123, but that's not why you would use the above pattern. this pattern is a convenience that allows to expose a public interface while totally hiding your implementation, like this example from the apple docs: `@syntheize age = numberOfYears;` it also allows me to do: `SomeAccount *account = [SomeAccount defaultAccount]; self.account = account;` without worrying about errors, because this is my style of writing code. it's purely convenience and coding style. – Salam Horani Dec 20 '11 at 03:59
0

it's a convention to keep the ivar (instance variables) safe, so you can only access it through the getter and setter.