Question

I've read about such problem in different sources: books, articles, etc but can't catch the idea of it. Could you give an example when the problem of backed i-vars and properties arise and how to solve it? And I've read the following code from here http://blog.bignerdranch.com/463-a-motivation-for-ivar-decorations.

@property (copy) NSString *name;
...
@synthesize name;
...
- (void) setPonyInfoFromPropertyDict: (NSDictionary *) ponyProperties {
    NSString *name = [ponyProperties objectForKey: @"Name"];
    ponyName = name; //self.ponyName=name should solve the problem? 
}

NSMutableString *mutableName = [NSMutableString stringWithString: @"Mikey"];
NSDictionary *ponyProperties = [NSDictionary dictionaryWithObject: mutableName  
                               forKey: @"Name"];
[pony setPonyInfoFromPropertyDict: ponyProperties];
[mutableName setString: @"Wookiee"]; 

Is that code and article relaited to this problem? And the right way is to use self.ponyName=name? In any other set methods?

Was it helpful?

Solution

I believe the example is wrong, or ill-posed. A proper example should be

@property (copy) NSString *ponyName; // not name!
...

@synthesize ponyName; // not name!
...
- (void) setPonyInfoFromPropertyDict: (NSDictionary *) ponyProperties {
    NSString *name = [ponyProperties objectForKey: @"Name"];
    ponyName = name;
    //self.ponyName = name; // will solve the problem 
}

name can be a mutable string, so if you want to make sure it won't change after it has been assigned you have to copy it. You can use a copy property for the task, but doing

ponyName = name;

you are not using the setter at all.

self.ponyName = name;

will fixe the issue and name will be copied by the setter.


Discussion

One of the most common mistakes when programming in Objective-C is to access the ivar directly instead of using a setter/getter.

Accessory methods are important since they typically provide a lot of logic for accessing and setting the ivars, most notably concerning memory management. Nowadays their implementation is greatly simplified by the ARC compiler, but, in case you use MRC, accessory methods are crucial in helping to handle the release/retain balance.

An example which is still valid under ARC, is when you declare a copy property. In such case the synthesized setter will take care of copying the argument before assigning to the ivar, so when you do

[self setMyCopyProperty:aNewThing];

or equivalently

self.myCopyProperty = aNewThing;

aNewThing will receive a copy message, before being assigned to the backed ivar.

On the other hand if you synthesize the property like this

@synthesize myCopyProperty;

you might accidentally find yourself doing

myCopyProperty = aNewThing;

which won't copy aNewThing, since you are accessing the ivar directly.

In order to not confuse ivars and accessory methods, it's then become a standard convention to prefix ivars with an underscore, i.e. synthesize them like

@synthesize myCopyProperty = _myCopyProperty;

In this way, if you accidentally do

myCopyProperty = aNewThing;

it won't compile as there's no such thing as a myCopyProperty ivar, probably saving you from a terrible headache when you are debugging memory-related issues. If instead you know what you are doing and you really want to access the ivar (perhaps in a init method), you can simply do

_myCopyProperty = [aNewThing copy];

which is a lot more explicit about your intention of not using a setter.

Finally, it's probably worth noting that with modern versions of the clang compiler you can avoid the explicit @synthesize as it will be automatically inserted in the form of

@synthesize foo = _foo;

Some exceptions apply, and you can read more about it here: When should I use @synthesize explicitly?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top