views:

1113

answers:

4

I'm rather confused about properties and instance variables in Objective-C.

I'm about half-way through Aaron Hillegass's "Cocoa Programming for Mac OS X" and everything is logical. You would declare a class something like this:

@class Something;

@interface MyClass : NSObject {
    NSString *name;
    NSArray *items;

    Something *something;

    IBOutlet NSTextField *myTextField;
}

@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSArray *items;
  • Since other objects need to manipulate our name and items instance variables, we use @property/@synthesize to generate accessors/mutators for them. Within our class, we don't use the accessors/mutators—we just interact with the instance variable directly.

  • something is just an instance variable that we're going to use in our class, and since no one else needs to use it, we don't create a pair of accessors and mutators for it.

  • We need to interact with a text field in our UI, so we declare an IBOutlet for it, connect it, and we're done.

All very logical.

However, in the iPhone world, things seem to be different. People declare properties for every single instance variable, declare properties for IBOutlets, and use accessors/mutators to interact with instance variables within the class (e.g. they would write [self setName:@"Test"] rather than name = @"Test").

Why? What is going on? Are these differences iPhone-specific? What are the advantages of declaring properties for all instance variables, declaring properties for IBOutlets, and using accessors/mutators within your own class?

+4  A: 

In the iPhone world, there's no garbage collector available. You'll have to carefully manage memory with reference counting. With that in mind, consider the difference between:

name = @"Test";

and

self.name = @"Test";
// which is equivalent to:
[self setName: @"Test"];

If you directly set the instance variable, without prior consideration, you'll lose the reference to the previous value and you can't adjust its retain count (you should have released it manually). If you access it through a property, it'll be handled automatically for you, along with incrementing the retain count of the newly assigned object.

The fundamental concept is not iPhone specific but it becomes crucial in an environment without the garbage collector.

Mehrdad Afshari
...but, in "Cocoa Programming for Mac OS X", Aaron Hillegass doesn't use the Garbage Collector... we manage memory manually...
Steve Harrison
Steve: I haven't seen the guide. Anyway, if you don't use properties and you assign to instance variables *anywhere*, whether it's within the class or not, you have to be careful about reference counts. Beside that, Mac OS X frameworks were older than iPhone counterparts. Objective-C 2.0 was already there when iPhone SDK came around, resulting in heavier usage of properties than OS X devs were used to. Overall, it's mostly a matter of style, rather than a strict rule.
Mehrdad Afshari
@Mehrdad: OK, that makes sense. Thanks!
Steve Harrison
+4  A: 

Properties are used to generate accessors for instance variables, there's no magic happening.

You can implement the same accessors by hand.

You can find in Aaron Hillegass's book examples of 3 memory management strategies for member variables. They are assign/copy/retain. You select one of those as required for given variable.

I assume you understand memory management in Objective-c ...

Accessors hide the complexity and differences of memory management for each variable.

For example:

name = @"Test"

is a simple assignment, name now holds reference to NSString @"Test". However you could decide to use copy or retain. No matter which version of memory management you chose accessor hides the complexity and you always access the variable with (or similar):

[self setName:@"Test"] 
[self name]

Now setName: might use assign/copy or retain and you don't have to worry about it.

My guess is that iPhone tutorials use properties to make it easier for new developers to jump through memory management (even though it's handy to generate appropriate accessors with properties rather than implement them by hand every time).

stefanB
OK, this makes sense too. Thanks!
Steve Harrison
+1  A: 

However, in the iPhone world, things seem to be different. People declare properties for every single instance variable, declare properties for IBOutlets, and use accessors/mutators to interact with instance variables within the class (e.g. they would write [self setName:@"Test"] rather than name = @"Test").

That's not iPhone-specific. Except in init methods and the dealloc method, it's good practice to always use your accessors. The main benefit, especially on the Mac (with Cocoa Bindings), is that using your accessors means free KVO notifications.

The reason why people “declare properties for every single instance variable” is most probably that all of their instance variables are things they want to expose as properties. If they had something they would want to keep private, they would not declare a property for it in the header file. (However, they may make a property for it in a class extension in the implementation file, in order to get the aforementioned free KVO notifications.)

Declaring properties for outlets is overkill, in my opinion. I don't see a point to it. If you don't make a property, the nib loader will set the outlet by direct instance-variable access, which is just fine for that task.

Peter Hosey
According to http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW6, if you don't retain top level objects after loading a nib on the iPhone, those objects will be deallocated. On OS X desktop top-level objects have a retain count of 1. But using properties (or at least setters/getters) makes it clearer what's going on, and it especially a good idea on the iPhone.
BJ Homer
I agree that it makes the retention explicit, but in this case, I don't think that's necessary–just remember that an outlet is always an owning relationship.
Peter Hosey
@Peter Hosey: Thanks! It's all starting to make sense now. One question, though: why shouldn't you use your accessor/mutator methods in the `init` method?
Steve Harrison
Steve: Because you haven't finished initializing the object yet. As such, sending yourself accessor messages is sending messages to a partially-initialized object. Same deal in `dealloc`, only in reverse: You've started deallocating the object, so you would be sending messages to a partially-deallocated object.
Peter Hosey
@Peter Hosey: Right, that makes sense. Thanks!
Steve Harrison
A: 

I would suggest that modern development has made a very strong attempt to identify, define and apply best practices.

Among these best practices we find continuity and consistency.

Apart from arguing over use of accessors in init and dealloc methods, accessors should generally be used all the time (inside and outside of a class) for the benefits they offer, including encapsulation, polymorphic var implementations (which both allow for abstracting and refactoring) and to facilitate those best practices of continuity and consistency. The fundamental benefits of an object-orient language come into play when doing things in this way and exploiting the fullness of the language's capabilities. Always being consistent in one's coding is an oft undermentioned benefit, as any senior programmer will usually attest.

JRT