Private ivars

I was skimming through the Stanford iPhone course last night (see previous post) and watched lecture 3 on Objective-C. One statement that caught my eye (see page 12 of the lecture notes) was the suggestion that you mark all of your instance variables @private. The instructor does not provide much of an explanation as to why he recommends this but it is worth spending a few minutes to ask if he is right and why?

Instance Variable Scope

Objective-C allows you to set four levels of scope for an instance variable as follows:

  • @private - accessible only within the class that declares it
  • @protected - accessible within the class that declares it and all subclasses. This is the default.
  • @package - accessible everywhere within the executable package (e.g. a framework library) but not outside
  • @public - accessible everywhere

Leaving aside the @package scope which is a bit of a special case useful when creating frameworks I hope you agree we can also eliminate @public as a possible choice. Making our instance variables public defeats the idea of data hiding or encapsulation which is a fundamental principle of object-oriented programming. Keeping the internal representation of an object “hidden” from the users of the class makes it much easier to change later as well as making the class more robust (since external users cannot mess with the internal object state).

So that leaves @private or @protected as possible choices. If you do nothing the default is protected which means any subclass of our class will be able to directly access the instance variables. This means that even if you have created getter and setter methods the subclass is not forced to use them to access the instance variables.

If on the other hand you explicitly mark an instance variable as @private a subclass no longer has direct access. For example, if I have a Person class defined as follows:

@interface Person : NSObject {
@private
  NSString *givenName;
  NSString *surname;
  NSString *fullname;
}

If I then subclass Person to create an Employee class:

@interface Employee : Person {
  ...
}

I cannot write an Employee method that directly accesses one of the Person instance variables:

    @implementation Employee
    - (void)logMyName {
      NSLog(@"%@",self.fullname);  // this will work
      NSLog(@"%@",fullname);       // this will not work
    }
    @end

If it is not clear why self.fullname is allowed in this case you may want to refer back to a previous post on understanding self. Remember that self.fullname is the same as [self fullname] which ends up trying to execute the getter method named fullname rather than directly accessing the instance variable. This assumes of course that the Person class has defined the getter method. If the getter method does not exist then even self.fullname would be invalid.

The advantage in this case of making the instance variables private is that we enforce access by the Employee class to be via the getter method. If at a later date we want to change the implementation of the Person class, perhaps by removing the ivar and generating the value from the givenName and surname, we can be sure that no code in the Employee class will break.

There is an additional and more subtle advantage to using @private to force the subclass to use the setter method. The setter method will often contain code to correctly manage and release memory or other logic to ensure only valid values can be set. If the subclass directly accesses the ivar it bypasses this code in the setter potentially introducing problems which can be tricky to track down.

Apple Guidelines

The Apple Coding Guidelines for Cocoa also make a couple of recommendations on declaring instance variables:

  • Avoid creating public instance variables. Developers should concern themselves with an object’s interface, not with the details of how it stores its data.
  • Explicitly declare instance variables either @private or @protected. If you expect that your class will be subclassed, and that these subclasses will require direct access to the data, use the @protected directive.

This is saying pretty much the same thing - avoid @public, use @protected if you need subclasses to have direct access to the ivar otherwise use @private.

Summary

So to come back to the original question, should you make all of your instance variables @private? There is an argument for using @protected if you are writing a class that you expect to be subclassed. However I would not do this because of some perceived performance advantage from allowing a subclass direct access. The performance overhead of calling getter and setter methods is in any case unlikely to have a noticeable impact on most Cocoa or iOS apps.

Perhaps the question is unfair as it is difficult to come up with a rule that always applies. Sooner or later a special case comes along which requires you to break the rule. However starting out with @private as the default and switching to @protected only when you really need to is a pretty good rule of thumb.