Cast an instance of a class to a @protocol in Objective-C
Solution 1
The correct way to do this is to do:
if ([self.myViewController conformsToProtocol:@protocol(MyProtocol)])
{
UIViewController <MyProtocol> *vc = (UIViewController <MyProtocol> *) self.myViewController;
[vc protocolMethod];
}
The UIViewController <MyProtocol> *
type-cast translates to "vc is a UIViewController object that conforms to MyProtocol", whereas using id <MyProtocol>
translates to "vc is an object of an unknown class that conforms to MyProtocol".
This way the compiler will give you proper type checking on vc
- the compiler will only give you a warning if any method that's not declared on either UIViewController
or <MyProtocol>
is called. id
should only be used in the situation if you don't know the class/type of the object being cast.
Solution 2
You can cast it like this:
if([self.myViewController conformsToProtocol:@protocol(MyProtocol)])
{
id<MyProtocol> p = (id<MyProtocol>)self.myViewController;
[p protocolMethod];
}
This threw me for a bit, too. In Objective-C, the protocol isn't the type itself, so you need to specify id
(or some other type, such as NSObject
) along with the protocol that you want.
Related videos on Youtube
Comments
-
Ford almost 2 years
I have an object (a UIViewController) which may or may not conform to a protocol I've defined.
I know I can determine if the object conforms to the protocol, then safely call the method:
if([self.myViewController conformsToProtocol:@protocol(MyProtocol)]) { [self.myViewController protocolMethod]; // <-- warning here }
However, XCode shows a warning:
warning 'UIViewController' may not respond to '-protocolMethod'
What's the right way to prevent this warning? I can't seem to cast
self.myViewController
as aMyProtocol
class. -
Ford over 15 yearsAh, cool, thanks. I just checked and saw that casting it as
(id)
works too. Is that bad form? -
TOMKA over 15 yearsIf you cast it as id<MyProtocol> then the compiler will warn you if you use methods that aren't defined in that protocol.
-
Andy over 15 years@dreamlax - This is how the compiler does type checking against protocols. See developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/… for more info.
-
Andy over 15 years@Ford - it would be better to use the the protocol specifically, since that way the compiler can perform some type checking for you.
-
Ford over 15 years@Andy, I don't think you need the '*' since 'id' is already a pointer. So: id<MyProtocol> p = (id<MyProtocol>)self.myViewController; [p protocolMethod]; Or just: [(id<MyProtocol>)self.myViewController protocolMethod];
-
memmons over 10 yearsWhen using protocols you really shouldn't care about the object type -- the whole point of a protocol is that any object type can adopt it and be used without having to cast to the specific object. So, I would recommend using the answer by @andy anywhere you are casting to a protocol instead of the above --
id<MyProtocol> p = (id<MyProtocol>)self.myViewController;
This answer and @andys are both correct, but his is more correct. -
Nick Forge over 10 years@Answerbot your comment is incorrect, and misses the point I made in the last paragraph of my answer. You might or might not care about the object type, it depends on the situation. What happens if you want to send a message declared on
UIViewController
tovc
in the example in my answer, and it's declared asid <MyProtocol>
? -
memmons over 10 yearsNot sure what in regards to my comment is incorrect? In any case, if you are checking if an object conforms to a protocol why would you then call some other method unrelated to the protocol? I can't recall ever needing to do this or seeing this in code I've reviewed. Seems like a code smell to me.
-
Nick Forge over 10 yearsJust because you haven't seen/used it, doesn't mean it's a code smell. Here's a code snippet showing one example of where throwing away type information by using
id
is a problem: gist.github.com/nsforge/7743616 -
nambatee over 6 yearsanyone knows why this puts in me in an infinite loop when called on a subclass of
UICollectionView
withif([self conformsToProtocol:@protocol(MyProtocol)])
?