Clean Objective-C: Private Methods in Objective-C

According to the TIOBE Programming Community Index (March 2013), Objective-C is the third most-popular programming language, behind Java and C. This is not surprising, as Objective-C is used to develop the thousands of Mac apps and 775,000 apps for iPhone, iPad, and iPod touch that are distributed via the App Store (Apple, as of January 2013). With more than 40 billion downloads, getting apps on the market as quickly as possible is highly attractive.

Unfortunately, the software’s (internal) quality often suffers from a short time to market. If the product’s lifecycle is longer than expected and technical debt is not paid off, development costs rise continuously. However, it is not that hard to build quality into the software right from the beginning. The new Clean Objective-C blog post series will demonstrate this. So, let’s get started…

Private Methods in Objective-C

Using private methods, we can generally hide the details or behaviors of a class from other classes. These other classes may be clients, Categories, or subclasses. If we extend a class, we have to make sure not to accidentally break its encapsulation. This blog post illustrates the latter problem by utilizing inheritance, and presents a solution (which is of course also valid for Categories).

From Public to Private Methods

To avoid exposing a method as part of a class’s API, we do not declare it within the @interface section but within the @implementation section.

We want to do this with the method makeActivationSound of our class Lightsaber:

#import <Foundation/Foundation.h>

@interface Lightsaber : NSObject

- (void)switchOn;

- (void)makeActivationSound;

@end

We need to remove makeActivationSound from our interface definition (Lightsaber.h):

#import <Foundation/Foundation.h>

@interface Lightsaber : NSObject

- (void)switchOn;

@end

We do not need to alter the implementation of our class (Lightsaber.m); here it is:

#import "Lightsaber.h"

@implementation Lightsaber

- (void)switchOn {
    [self makeActivationSound];
    // ...
}

- (void)makeActivationSound {
    NSLog(@"BB-ZSHOOOO");
}

@end

Now, a private method like makeActivationSound can no longer be called by another class. A method call from a Jedi class like…

[[Lightsaber new] makeActivationSound];

…is no longer possible:

Compiler error: Method 'makeActivationSound' is defined in class
'Lightsaber' and is not visible.

(Using the performSelector method or the Objective-C Runtime library, we can still invoke private methods.)

So far, so good. We can call the switchOn method to see the turn-on sound effect of the lightsaber:

[[Lightsaber new] switchOn]; // -> BB-ZSHOOOO

An Unexpected Behavior

Just imagine we have lost our lightsaber. A padawan finds it in the desert. As he is technically well-versed, he wants to extend our lightsaber with a second plasma blade. Therefore, he creates the subclass DoubleBladedLightsaber:

// DoubleBladedLightsaber.h
#import <Foundation/Foundation.h>
#import "Lightsaber.h"

@interface DoubleBladedLightsaber : Lightsaber
@end

When switched on, the new plasma blade makes a BB-ZSHUUUU sound instead of BB-ZSHOOOO. Without knowing how the sound of the old plasma blade is created, the padawan decides to add a BB-ZSHUUUU sound effect creation functionality which he names makeActivationSound. This one he uses together with the existing switch-on functionality when turning on the double-bladed lightsaber:

// DoubleBladedLightsaber.m
#import "DoubleBladedLightsaber.h"

@implementation DoubleBladedLightsaber

- (void)switchOn {
    [super switchOn];
    [self makeActivationSound];
}

- (void)makeActivationSound {
    NSLog(@"BB-ZSHUUUU");
}

@end

Bursting with curiosity, he pushes the activation button to hear the cool BB-ZSHOOOO, BB-ZSHUUUU sounds…

[[DoubleBladedLightsaber new] switchOn];
// -> BB-ZSHUUUU
// -> BB-ZSHUUUU

But what was that? The sound of the old plasma blade has changed! It is no longer producing BB-ZSHOOOO but instead sounds like the new blade: BB-ZSHUUUU! How is that possible?

The method switchOn of the class Lightsaber has called the implementation of the method makeActivationSound of the subclass DoubleBladedLightsaber. The implementation of makeActivationSound of the class Lightsaber was overriden by the class DoubleBladedLightsaber. Private methods in Objective-C are not as private as in other modern object-oriented programming languages. They are semi-private and have polymorphic behavior.

The Risk… and Its Reduction

That poses a certain risk: we can (accidentally) compromise the implementation of our extended class. The probability of unknowingly overriding a private method rises when we extend a framework or library class without having access to its source code. (Apple’s Cocoa classes immediately come to mind.) Using our lightsaber, the innocent padawan has fallen into this “trap”.

To reduce the risk of accidental method overriding, we should add a prefix to the names of private methods that is as unique as possible. The most obvious prefix is probably an underscore (_) but that is already used by Apple for its private Cocoa methods.

For this reason, one should use an uppercase abbreviation of the company and/or the product name. In our example that could be the prefix AQN_LS_ for the company akquinet and the product Lightsaber:

#import "Lightsaber.h"

@implementation Lightsaber

- (void)switchOn {
    [self AQN_LS_makeActivationSound];
    // ...
}

- (void)AQN_LS_makeActivationSound {
    NSLog(@"BB-ZSHOOOO");
}

@end

After this modification, the padawan can finally hear a BZ-SHOOOO, BZ-SHUUUU when switching on his double-bladed lightsaber. May the force be with him!

So, the risk of accidentally overriding a private method depends on the uniqueness of its name.

Finally, it should be mentioned that Apple does point out the problem, e.g. in Coding Guidelines for Cocoa.

tl;dr

  • As private methods can be overridden in Objective-C, there is a risk of compromising the implementation of an extended class.
  • Therefore, always use an application-specific unique prefix for the names of private methods.
  • A single underscore character (_) may not be used as a prefix, as this is reserved for Cocoa classes.

3 thoughts on “Clean Objective-C: Private Methods in Objective-C

  1. Great tip, thanks! I’ve used the ability to override private methods for tests objects already, but haven’t given a single thought to the risk this poses.

  2. Hey there, You contain done a top occupation. I will certainly digg it and personally plug to my buddies. I’m trusty they will probably be benefited from this website.

Comments are closed.