107

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 a MyProtocol class.

Ford
  • 1,485
  • 3
  • 14
  • 20

2 Answers2

182

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.

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Nick Forge
  • 21,344
  • 7
  • 55
  • 78
  • 2
    When 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 p = (id)self.myViewController;` This answer and @andys are both correct, but his is _more_ correct. – memmons Nov 30 '13 at 17:13
  • 2
    @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` to `vc` in the example in my answer, and it's declared as `id `? – Nick Forge Dec 01 '13 at 15:41
  • Not 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. – memmons Dec 01 '13 at 16:14
  • Just 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: https://gist.github.com/nsforge/7743616 – Nick Forge Dec 02 '13 at 01:43
65

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.

Andy
  • 30,088
  • 6
  • 78
  • 89
  • Ah, cool, thanks. I just checked and saw that casting it as `(id)` works too. Is that bad form? – Ford Mar 06 '09 at 04:03
  • 1
    If you cast it as id then the compiler will warn you if you use methods that aren't defined in that protocol. – dreamlax Mar 06 '09 at 04:09
  • 1
    @dreamlax - This is how the compiler does type checking against protocols. See http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/chapter_7_section_11.html#//apple_ref/doc/uid/TP30001163-CH15-TPXREF151 for more info. – Andy Mar 06 '09 at 17:50
  • 1
    @Ford - it would be better to use the the protocol specifically, since that way the compiler can perform some type checking for you. – Andy Mar 06 '09 at 18:56
  • 1
    @Andy, I don't think you need the '*' since 'id' is already a pointer. So: id p = (id)self.myViewController; [p protocolMethod]; Or just: [(id)self.myViewController protocolMethod]; – Ford Mar 07 '09 at 01:43
  • anyone knows why this puts in me in an infinite loop when called on a subclass of `UICollectionView` with `if([self conformsToProtocol:@protocol(MyProtocol)])`? – nambatee Oct 26 '17 at 12:57