id<Protocol>, Retain/Release and Protocol Inheritance
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;
@end
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;
@end
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;
@end
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;
@end
Now an object of type id<MyProtocol>
will accept messages such as retain
, release
or even performSelector:withObject:afterDelay:
without generating compiler warnings.
Very interesting, I guess you learn something new every day. Thanks for this.
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!
??????? ? ???????.
Just wanted to say thanks. I was puzzled by this and you solved it.
Ditto Mike V – That was very handy (and well-written). Thanks!