82

What is the semantic difference between these 3 ways of using ivars and properties in Objective-C?

1.

@class MyOtherObject; 
@interface MyObject {
}
@property (nonatomic, retain) MyOtherObject *otherObj;

2.

#import "MyOtherObject.h"
@interface MyObject {
    MyOtherObject *otherObj;
}
@property (nonatomic, retain) MyOtherObject *otherObj;

3.

#import "MyOtherObject.h"
@interface MyObject {
    MyOtherObject *otherObj;
}
Hunter Monk
  • 1,767
  • 13
  • 25
ennuikiller
  • 43,779
  • 13
  • 108
  • 136

2 Answers2

57

Number 1 differs from the other two by forward declaring the MyOtherObject class to minimize the amount of code seen by the compiler and linker and also potentially avoid circular references. If you do it this way remember to put the #import into the .m file.

By declaring an @property, (and matching @synthesize in the .m) file, you auto-generate accessor methods with the memory semantics handled how you specify. The rule of thumb for most objects is Retain, but NSStrings, for instance should use Copy. Whereas Singletons and Delegates should usually use Assign. Hand-writing accessors is tedious and error-prone so this saves a lot of typing and dumb bugs.

Also, declaring a synthesized property lets you call an accessor method using dot notation like this:

self.otherObj = someOtherNewObject; // set it  
MyOtherObject *thingee = self.otherObj; // get it 

Instead of the normal, message-passing way:

[self setOtherObject:someOtherNewObject]; // set it
MyOtherObject *thingee = [self otherObj]; // get it 

Behind the scenes you're really calling a method that looks like this:

- (void) setOtherObj:(MyOtherObject *)anOtherObject {

    if (otherObject == anOtherObject) {
        return;  
    }

    MyOtherObject *oldOtherObject = otherObject; // keep a reference to the old value for a second
    otherObject = [anOtherObject retain]; // put the new value in  
    [oldOtherObject release]; // let go of the old object
} // set it

…or this

- (MyOtherObject *) otherObject {  
    return otherObject;
} // get it

Total pain in the butt, right. Now do that for every ivar in the class. If you don't do it exactly right, you get a memory leak. Best to just let the compiler do the work.

I see that Number 1 doesn't have an ivar. Assuming that's not a typo, it's fine because the @property / @synthesize directives will declare an ivar for you as well, behind the scenes. I believe this is new for Mac OS X - Snow Leopard and iOS4.

Number 3 does not have those accessors generated so you have to write them yourself. If you want your accessor methods to have side effects, you do your standard memory management dance, as shown above, then do whatever side work you need to, inside the accessor method. If you synthesize a property as well as write your own, then your version has priority.

Did I cover everything?

Nicolas Miari
  • 15,044
  • 6
  • 72
  • 173
willc2
  • 36,081
  • 24
  • 84
  • 99
  • yes, thank you very much! One note I'd like to make is that if you take out the forward class pragma in #1 and replace it with an #import "MyOtherObject" you get a compile time error, not sure why though .... – ennuikiller Nov 13 '10 at 16:56
  • is there any advantage of using approach #2 over approach #1? – Greg Nov 08 '11 at 03:18
  • @Greg Method #1 will prevent a circular reference. See http://stackoverflow.com/questions/7221174/iphone-objective-c-circular-reference-problem – willc2 Jul 12 '12 at 22:23
  • 3
    Nice answer except the bit about dot notation. You do not need to synthesise the property to use it for dot notation. In fact, you do not need to declare a property at all. As long as you have a declared setter and getter (e.g. `setFoo:` and `foo`) you can use dot notation. – JeremyP Aug 05 '12 at 20:09
  • For relevance, if using ARC, synthesize is done automatically. – Sean Larkin Dec 09 '13 at 12:46
17

Back in the old days you had ivars, and if you wanted to let some other class set or read them then you had to define a getter (i.e., -(NSString *)foo) and a setter (i.e., -(void)setFoo:(NSString *)aFoo;).

What properties give you is the setter and getter for free (almost!) along with an ivar. So when you define a property now, you can set the atomicity (do you want to allow multiple setting actions from multiple threads, for instance), as well as assign/retain/copy semantics (that is, should the setter copy the new value or just save the current value - important if another class is trying to set your string property with a mutable string which might get changed later).

This is what @synthesize does. Many people leave the ivar name the same, but you can change it when you write your synthesize statement (i.e., @synthesize foo=_foo; means make an ivar named _foo for the property foo, so if you want to read or write this property and you do not use self.foo, you will have to use _foo = ... - it just helps you catch direct references to the ivar if you wanted to only go through the setter and getter).

As of Xcode 4.6, you do not need to use the @synthesize statement - the compiler will do it automatically and by default will prepend the ivar's name with _.

David H
  • 39,114
  • 12
  • 86
  • 125
  • 1
    It should be noted that the atomicity of a property [does not guarantee thread safety](http://stackoverflow.com/a/589392/603977). – jscs Aug 05 '12 at 19:54
  • So if I have an ivar that is atomic, you mean that while the setter is setting it or the getter is getting it, another thread kicks in and tries to do either, that it all gets foobared? Then what is the point of atomic? My understanding is that atomic at least makes sure that if you set an ivar, it gets set, its retain count is appropriate etc. Otherwise why atomic? [Not that it solves all problems just prevents you from getting foobared] – David H Aug 05 '12 at 22:47
  • 2
    You're guaranteed to get a valid, whole object -- the getter won't return an object that is in the process of being deallocated -- but if another threads is using the setter, you may get the value from before or after. Specifying that has to be handled outside the getters and setters. In other words, no thread will be interrupted _during_ the getter or setter operation, but the order of the operations is not (cannot be, at this level, AFAIK) defined. – jscs Aug 05 '12 at 23:27
  • Well, I would argue that your original comment was misplaced - the atomicity is honored, its just that accessing via threads can lead to a raft of problems - thus, every ivar I've ever declared is atomic and if there are threads involved then concurrency is dealt with elsewhere. – David H Aug 06 '12 at 00:10