id<Protocol>, Retain/Release and Protocol Inheritance

On May 19, 2009

Paul Cantrell’s recent post showed that you can use Objective-C protocols to constrain the behavior of id objects. When declaring an instance variable or parameter as id<Protocol>, the compiler’s type checker will warn when sending message not found in Protocol.

For this reason, protocols are pretty handy. But in practice, a problem arises. Let’s say you have a protocol defined as follows:

@protocol MyProtocol
- (void)someMessage;

Now let’s say you have a member variable defined as conforming to MyProtocol:

id<MyProtocol> someVar;

What happens if you try to retain or release the object? The compiler gives you a warning, because MyProtocol doesn’t declare the retain or release messages.

So what do we do about this? Perhaps your first instict is to manually declare the retain and release messages in your protocol:

@protocol MyProtocol
- (void)someMessage;
- (id)retain;
- (oneway void)release;

But that seems tedious and hackish, doesn’t it? It’s hard to believe that would be the “correct” solution.

Or maybe you could declare someVar slightly differently, like this:

NSObject<MyProtocol> *someVar;

This will get rid of the compiler warnings, but the syntax is pretty unwieldy and it still doesn’t feel right. None of the Apple-supplied classes use this syntax, after all.

You may have noticed that in addition to the NSObject class there’s an NSObject protocol which declares most of the methods found in NSObject. Using this knowledge, you could define someVar
a little more elegantly:

id<MyProtocol,NSObject> *someVar;

But it still feels like we’re not there yet. Again, Apple doesn’t appear to use this form of declaration in their own classes.

Enter protocol inheritance. You can declare a protocol to inherit from another protocol as follows:

@protocol Inherits<Base>
- (void)foo;

The above snippet declares a protocol named Inherits containing a single method, foo. In addition, the Inherits protocol extends a protocol named Base, thus all methods of base Base are included in Inherits as well.

Circling back to our original problem, the solution is now obvious: make MyProtocol inherit from NSObject:

@protocol MyProtocol<NSObject>
- (void)someMessage;

Now an object of type id<MyProtocol> will accept messages such as retain, release or even performSelector:withObject:afterDelay: without generating compiler warnings.

0 responses to “id<Protocol>, Retain/Release and Protocol Inheritance”

  1. Maniacdev says:

    Very interesting, I guess you learn something new every day. Thanks for this.

  2. pTracker says:

    Agreed (very interesting). Protocol inheritence is one of those things that make a lot of sense once you know about it, but don’t even suspect it’s there. Thanks!

  3. clelpersite says:

    ??????? ? ???????.

  4. Mike V says:

    Just wanted to say thanks. I was puzzled by this and you solved it.

  5. Kris says:

    Ditto Mike V – That was very handy (and well-written). Thanks!