I have been reading about Protocols on Objective-C but I cannot grasp this:
Consider this line
Person <CoordinateSupport> *person = [[Person alloc] init];
What is the purpose of declaring the variable to conform to the protocol CoordinateSupport
? Is this something just for compile time, so Xcode can warn me if I assign something different to person
or is there any purpose at run time?
I cannot see how a variable can conform to a protocol. OK, a class is easy to see, because you can have a protocol defining methods that you want some class to follow but an ivar?
I am not seeing it.
The standard pattern when declaring that a variable conforms to a protocol is to give it the "any object" type, id
. Declaring that a variable both has a specific type and conforms to a protocol is typically redundant – I'll explain why later. For now, let's talk about variables of type id<P>
, where P
is some protocol, and why they're useful. This type should be read as "an instance of any class that conforms to P
."
To concretize the discussion that follows, let's define a protocol:
@protocol Adder
- (NSInteger)add:(NSInteger)a to:(NSInteger)b;
@end
I cannot see how a variable can conform to a protocol.
This one is easy. A variable conforms to an Objective-C protocol when it represents an instance of a class that implements all of the required methods in the protocol.
@interface Abacus : NSObject <Adder>
@end
@implementation Abacus
- (NSInteger)add:(NSInteger)a to:(NSInteger)b { return a + b; }
- (NSInteger)beadCount { return 91; }
@end
Given this Abacus
class, you could, of course, create a new Abacus
:
Abacus *a = [[Abacus alloc] init];
NSLog(@"%ld", (long)[a add:5 to:6]); // 11
NSLog(@"%ld", (long)[a beadCount]); // 91
But you could also declare a
to just be of type id<Adder
. Remember, that means the type of a
is "an instance of any class that conforms to Adder
."
id<Adder> a = [[Abacus alloc] init];
NSLog(@"%ld", (long)[a add:5 to:6]); // 11
NSLog(@"%ld", (long)[a beadCount]); // Compile error: No known instance method for selector 'beadCount'
The compiler complains because all we said about the type of a
is that it is a class that conforms to Adder
, and nowhere in the Adder
protocol do we say anything about a method named beadCount
.
What is the purpose of declaring the variable to conform to [a protocol]?
The purpose is for information hiding. When you want a class that conforms to Adder
, you don't need to care about what the actual class is – you just get an id<Adder>
. Imagine that Abacus
is a system class, and you've written the following code:
- (Abacus *)getAdder { return [[Abacus alloc] init]; }
- (void)doWork {
Abacus *a = [self getAdder];
// Do lots of adding...
}
Then, in iOS 42, Apple comes up with a new innovation – the Calculator
class! Your friends tell you that Calculator
adds two numbers together more than twice as fast as Abacus
, and all the cool kids are using it! You decide to refactor your code, but you realize that not only do you have to change the return type of getAdder
, but also the types of all the variables to which you assign the return value of getAdder
! Lame. What if you had done this instead:
- (id<Adder>)getAdder { return [[Abacus alloc] init]; }
- (void)doWork {
id<Adder> *a = [self getAdder];
// Do lots of adding...
}
Now, when you want to migrate to Calculator
, you just need to change the body of getAdder
to return [[Calculator alloc] init]
and you're done! One line. The rest of your code stays exactly the same. In that case, you have hidden the true type of the instance returned from getAdder
from the rest of your code. Information hiding makes refactoring easier.
Lastly, I promised to explain why something like Abacus <Adder> *a = ...
is usually redundant. What you're saying here is "a
is an instance of Abacus
that conforms to Adder
." But you (and the compiler) already know that Abacus
conforms to Adder
– it's right there in the interface declaration! As rmaddy points out, there are some cases where you want to talk about an instance that is either a given class, or a subclass thereof, and also specify that it conforms to a protocol, but those situations are rare, and most often specifying both a class and protocol conformance is unneeded.
For more information, check out Apple's Working with Protcols guide.