32

I'm trying to understand how strategies some folks use to distinguish instance vars vs. properties. A common pattern is the following:

@interface MyClass : NSObject {
    NSString *_myVar;
}

@property (nonatomic, retain) NSString *myVar;
@end

@implementation MyClass
@synthesize myVar = _myVar;

Now, I thought the entire premise behind this strategy is so that one can easily distinguish the difference between an ivar and property. So, if I want to use the memory management inherited by a synthesized property, I'd use something such as:

myVar = @"Foo";

The other way would be referencing it via self.[ivar/property here].

The problem with using the @synthesize myVar = _myVar strategy, is I figured that writing code such as:

myVar = some_other_object; // doesn't work. 

The compiler complains that myVar is undeclared. Why is that the case?

Thanks.

7 Answers7

28

Properties are just setters and getters for ivars and should (almost) always be used instead of direct access.

@interface APerson : NSObject {
    // NSString *_name;           //  necessary for legacy runtime
}

@property(readwrite) NSString *name;

@end

@implementation APerson
@synthesize name;                 // use name = _name for legacy runtime
@end

@synthesize creates in this case those two methods (not 100% accurate):

- (NSString *)name {
    return [[_name copy] autorelease];
}
- (void)setName:(NSString *)value {
    [value retain];
    [_name release];
    _name = value;
}

It's easy now to distinguish between ivars and getters/setters. The accessors have got the self. prefix. You shouldn't access the variables directly anyway.


Your sample code doesn't work as it should be:

_myVar = some_other_object;      // _myVar is the ivar, not myVar.
self.myVar = some_other_object;  // works too, uses the accessors
Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
  • 2
    Not to resurrect an old post, but I was wondering about this very topic when I saw this question. This seems like a nice explanation but for one situation which struck me. If I do something like `[someObject.someArray addObject:anotherObject]`, wouldn't that mean that the object `anotherObject` is being added to a temporary copy of the array `someArray` and wouldn't affect the actual property? – eric.mitchell Jun 08 '12 at 01:40
  • @Rickay: The default behaviour is to copy, such that an external object can't modify the internal state. You can however change `copy` to `retain` if you want to offer a mutable array. – Georg Schölly Jun 08 '12 at 10:03
  • 2
    Actually your statement about _myVar being reserved by Apple is 100% false and come Mountain Lion it will be the default synthesize behavior to have the backing ivar be _ivar. You're thinking of _methodName that Apple says not to use. – Lucas Derraugh Jun 27 '12 at 04:37
  • 2
    @Lucas: You're absolutely right. I'm going to change it. For everyone interested in this, Apple even [encourages the underscore](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingIvarsAndTypes.html). – Georg Schölly Jun 28 '12 at 20:17
  • @Lucas: I've been looking at the [obj-c spec](http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW3) and nowhere it states that `_ivarName` is going to be created. I think if you're going to use it directly you have to specify it. Maybe this changes with Mountain Lion, but I think it's strange to have `_someVar` implicitly declared. – Georg Schölly Jun 28 '12 at 20:30
  • @GeorgSchölly It was talked about in Apple dev forums and I've tested it on Mountain Lion's Xcode build. So we'll see in a few weeks, but I'm pretty sure it's becoming the default because they will be automatically synthesizing the properties with that backing iVar. I'll see if I can find some definite answer on this though, maybe WWDC videos have the answer ;) – Lucas Derraugh Jun 28 '12 at 22:20
  • 9
    Officially in the WWDC 2012 video Session 405 - Modern Obj-C around 11 minutes. property declaration is all you need and you get the synthesize for free with the backing instance variable being underscored. – Lucas Derraugh Jul 12 '12 at 04:07
  • @Lucas what about memory management and stuffs (considering we have ARC enabled) – Van Du Tran May 24 '13 at 17:14
  • @VanDuTran I'm not sure what your question is? Memory management will behave the same way as if you were to use synthesize under ARC. – Lucas Derraugh May 24 '13 at 18:57
  • @Lucas well, before ARC, I remember that when you override setters you have to carefully do retain, release, autorelease and stuffs properly.. because that's what synthesize does for you.. that's why there was an advantage to use the properties vs instance variable -> where no retain, release, autorelease code is handled for you.. – Van Du Tran Jun 04 '13 at 22:08
8

A synthesized property named prop is actually represented by two methods prop (returning the current value of the property) and setProp: (setting a new value for prop).

The self.prop syntax is syntactic sugar for calling one of these accessors. In your example, you can do any one of the following to set the property myVar:

self.myVar = @"foo"; // handles retain/release as specified by your property declaration
[self setMyVar: @"foo"]; // handle retain/release
_myVar = @"Foo"; // does not release old object and does not retain the new object

To access properties, use self.propname. To access instance variables use just the instance variable's name.

rincewind
  • 2,502
  • 19
  • 26
5

The problem with using the @synthesize myVar = _myVar strategy, is I figured that writing code such as:

myVar = some_other_object; // doesn't work.

The compiler complains that myVar is undeclared. Why is that the case?

Because the variable myVar is undeclared.

That statement uses the syntax to access a variable, be it an instance variable or some other kind. As rincewind told you, to access a property, you must use either the property-access syntax (self.myVar = someOtherObject) or an explicit message to the accessor method ([self setMyVar:someOtherObject]).

Otherwise, you're attempting to access a variable, and since you don't have a variable named myVar, you're attempting to access a variable that doesn't exist.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
2

In general, I name my properties the same as my instance variables; this is the default assumption that the @property syntax makes. If you find you're fighting the defaults, you're doing it wrong (or your framework sux, which is not the case for Cocoa/Cocoa-touch in my opinion).

The compiler error you're getting is because property use always has to have an object reference, even inside your own class implementation:

self.stuff = @"foo"; // property setter
[stuff release]; // instance variable
stuff = @"bar"; // instance variable
return self.stuff; // property getter

I know that many Cocoa programmers disagree, but I think it's bad practice to use properties inside your class implementation. I'd rather see something like this:

-(void) someActionWithStuff: (NSString*) theStuff {
    // do something
    [stuff release];
    stuff = [theStuff copy];
    // do something else
}

than this:

-(void) someActionWithStuff: (NSString*) theStuff {
    // do something
    self.stuff = theStuff;
    // do something else
}

I prefer to do memory management as explicitly as possible. But even if you disagree, using the self.stuff form will clue in any experienced Objective-C programmer that you're calling a property rather than accessing an instance variable. It's a subtle point that's easy for beginners to gloss over, but after you've worked with Objective-C 2.0 for a while, it's pretty clear.

Don McCaughey
  • 9,532
  • 3
  • 30
  • 36
  • The problem with doing it as you describe is you leave yourself open to bugs when you duplicate the memory management code as you've shown (for example, your fitst version of someActionWithStuff will crash if you do [self someActionWithStuff:stuff]!). Further, if you change the behaviour of setStuff, you're left wondering where else you might need to apply the change. – Peter N Lewis Jul 31 '09 at 08:46
  • 4
    “… it's bad practice to use properties inside your class implementation.” No, it's not. Using the accessors (whether by property-access syntax or explicitly) causes KVO notifications for free. Hitting the instance variable directly means you have to post your own KVO notifications, which you *will* forget to do in at least one place. – Peter Hosey Jul 31 '09 at 16:44
  • If using accessors from inside the class implementation is fine, does that mean that you have @property accessors for _every_ ivar? On the one side this sounds e.g. for lazy-loading something in accessors or the automatic retain/release management of @synthesize. But then, wouldn't that mean that private ivars that contain internal states (such as maybe lock and condition objects) are exposed to other classes? (Accessors are methods and from what I remember, methods are always public in Objective-C) – Zargony Jul 31 '09 at 19:55
0

Don,

According to the "rules", you should call Release for every Copy, Alloc, and Retain. So why are you calling Release on stuff? Is this assuming it was created using Alloc, Copy, or Retain?

This brings up another question: Is it harmful to call Release on a reference to an object if it's already been released?

Edward An
  • 1,727
  • 5
  • 15
  • 16
  • Don is releasing the ivar stuff because a copy is made when it is assigned (see the following line). This is (more or less) the same beaviour as the @synthesized property setter when used with the copy (or similarly, retain) attribute. And yes, it is absolutely harmful to call release on a reference after you've called release, unless you have two ownerships of the object to release. See the memory management rules at . – Peter N Lewis Jul 31 '09 at 08:49
  • So now Releasing a reference pointer involves more than just the immediate Alloc, Copy, and Retain? Instead, references should be release prior to a new reference assignment (unless of course the self prop is used, which performs this under the covers)? This seems wrong. But please confirm as i chew on this more. Thanks! – Edward An Jul 31 '09 at 21:39
  • After a little bit of mental visualization, this makes sense. the reference count must be manually managed, hence the Release before assigning it a new ref. – Edward An Jul 31 '09 at 21:51
0

Since Apple reserves the _ prefix for itself, and since I prefer to make it more obvious when I am using the setter and when I am using the ivar, I have adopted the practive of using a prefix of i_ on my ivars, so for example:

@interface MyClass : NSObject {
    NSString *i_myVar;
}

@property (nonatomic, retain) NSString *myVar;

@synthesize myVar = i_myVar;

i_myVar = [input retain];
self.myVar = anotherInput;
[i_myVar release]

Since it is quite important to know when you are using the setter and when you are using the ivar, I find the explicitly different name is safer.

In your question, it should be:

self.myVar = @"Foo"; // with setter, equivalent to [self setMyVar:@"Foo"]

and

_myVar = some_other_object;  // direct ivar access - no memory management!

Remember that you should not use setters/getters in init/dealloc, so you need to do your direct ivar access (and careful memory management) iin those methods.

Peter N Lewis
  • 17,664
  • 2
  • 43
  • 56
0

what's wrong with simply using

@interface MyClass : NSObject
@property NSString *prop;
@end

nonatomic and retain are not required, retain is the default, and atomic/nonatomic isn\t important unless XCode tells you with a warning.

it is NOT necessary to declare the iVar, one will be created for you named _prop, if you really want to use one (i don't see why to be honest)

@synthesize is NOT required.

when (and you should) using ARC you don't have to bother with retain and release either.

keep it simple !

furthermore, if you have a method like this one

- (void)aMethod:(NSString*)string
{
    self.prop = string;
    // shows very clearly that we are setting the property of our object

    _aName = string;
    // what is _aName ? the _ is a convention, not a real visual help
}

i would always use properties, more flexible, easier to read.

Pizzaiola Gorgonzola
  • 2,789
  • 1
  • 17
  • 12