The Benefits of Coding Protocols that *Extend* the NSObject Protocol

by Clint on July 13, 2010

Update (8/19/10): It’s come to my attention that using delegates as the primary example below was not a good idea. When you have a @property for a delegate, the best practice is to use assign, not retain, so that you avoid a so-called “retain loop” and accompanying memory leaks (more info). That said, the idea of extending the NSObject protocol so that you can call release without warnings is still helpful in some situations–just not those involving delegates.

It’s pretty common to use custom Objective-C protocols in iPhone development, especially when it comes to using delegates to keep your code modular. Consider the following example:

@protocol EngineDelegate
{
    -(void) goFaster;
    -(void) goSlower;
}

@interface Automobile : NSObject
{
    id engineDelegate;
}
@property (nonatomic, assign) id engineDelegate;

This shows an Automobile class that has a single member variable/property, an “engine delegate” (i.e., the Automobile will delegate requests to go faster/slower to the EngineDelegate). Because engineDelegate is declared using the generic pointer “id” type, you can assign ANY object to it as long as that object implements the EngineDelegate protocol (i.e., has -goFaster and -goSlower methods). But what happens if you try to release the delegate? For example:

@implementation Automobile
...
- (void) dealloc
{
    [engineDelegate release];
}
@end

The compiler will generate a “-release not found” warning because it can’t tell if engineRelease implements the -release method. This is problematic not just because of the warning, but more importantly, because it prevents you from doing normal retain/release operations that are common in a managed memory environment like the iPhone. In other words, how are you supposed to ensure that the delegate is properly released?

I recently came across a clever solution to this dilemma on MobileOrchard. In a nutshell, the post suggests declaring protocols as inheriting from the NSObject protocol which, in turns, defines the retain and release methods. A lot of developers might be surprised to learn that you can do such a thing (I certainly was). Here’s what it looks like:

@protocol EngineDelegate 
{
    -(void) goFaster;
    -(void) goSlower;
}

Now the Automobile class can safely assume that the delegate will implement the -retain and -release methods (and in most cases, the delegate will inherit the methods from the NSObject*). It’s nice that we can get rid of compiler warnings in this way, but it’s more important that we can use normal retain/release methods for memory management and not worry about whether or not the memory used by the delegate is freed.

*Note that not all objects extend the NSObject class (although for most folks, this is pretty rare). For example, many classes related to Cocoa’s “distributed object” mechanism descent from NSProxy instead of NSObject.

Clint Harris is an independent software consultant living in Brooklyn, New York. He can be contacted directly at ten.sirrahtnilc@tnilc.