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:

[code lang=”objc” highlight=”7″]
#import <Foundation/Foundation.h>

@interface Lightsaber : NSObject

– (void)switchOn;

– (void)makeActivationSound;

@end
[/code]

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

[code lang=”objc”]
#import <Foundation/Foundation.h>

@interface Lightsaber : NSObject

– (void)switchOn;

@end
[/code]

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

[code lang=”objc” highlight=”10,11,12″]
#import "Lightsaber.h"

@implementation Lightsaber

– (void)switchOn {
[self makeActivationSound];
// …
}

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

@end
[/code]

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

[code lang=”objc” highlight=”1″]
[[Lightsaber new] makeActivationSound];
[/code]

…is no longer possible:

[code light=”true” highlight=”1,2″]
Compiler error: Method ‘makeActivationSound’ is defined in class
‘Lightsaber’ and is not visible.
[/code]

(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:

[code lang=”objc” highlight=”1″]
[[Lightsaber new] switchOn]; // -> BB-ZSHOOOO
[/code]

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:

[code lang=”objc” highlight=”5″]
// DoubleBladedLightsaber.h
#import <Foundation/Foundation.h>
#import "Lightsaber.h"

@interface DoubleBladedLightsaber : Lightsaber
@end
[/code]

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:

[code lang=”objc” highlight=”6,7,8,9,11,12,13″]
// DoubleBladedLightsaber.m
#import "DoubleBladedLightsaber.h"

@implementation DoubleBladedLightsaber

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

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

@end
[/code]

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

[code lang=”objc” highlight=”1″]
[[DoubleBladedLightsaber new] switchOn];
// -> BB-ZSHUUUU
// -> BB-ZSHUUUU
[/code]

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:

[code lang=”objc” highlight=”6,10″]
#import "Lightsaber.h"

@implementation Lightsaber

– (void)switchOn {
[self AQN_LS_makeActivationSound];
// …
}

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

@end
[/code]

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.