在Objective-C中将一个类的实例转换为@protocol

107

我有一个对象(一个UIViewController),该对象可能符合我定义的协议,也可能不符合。

我知道可以确定对象是否符合协议,然后安全地调用该方法:

if([self.myViewController conformsToProtocol:@protocol(MyProtocol)]) {
    [self.myViewController protocolMethod]; // <-- warning here
}

然而,XCode 显示了一个警告:

warning 'UIViewController' may not respond to '-protocolMethod'

如何避免这个警告?我似乎无法将self.myViewController强制转换为MyProtocol类。

2个回答

182
这个问题的正确做法是:
if ([self.myViewController conformsToProtocol:@protocol(MyProtocol)])
{
        UIViewController <MyProtocol> *vc = (UIViewController <MyProtocol> *) self.myViewController;
        [vc protocolMethod];
}

UIViewController <MyProtocol> * 这种类型转换意味着"vc是一个符合MyProtocol协议的UIViewController对象",而使用id <MyProtocol>则意味着"vc是一个未知类别但符合MyProtocol协议的对象"。

这样编译器会为vc提供适当的类型检查-如果调用了没有在UIViewController或者<MyProtocol>中声明的方法,则编译器只会给出警告。只有在不知道对象的类/类型的情况下才应该使用id


2
当使用协议时,您真的不应该关心对象类型--协议的整个重点是任何对象类型都可以采用它并且可以在不必转换为特定对象的情况下使用。因此,我建议在任何需要将其转换为协议的地方使用@andy的答案,而不是上面的代码 - id<MyProtocol> p = (id<MyProtocol>)self.myViewController; 这个答案和@andy的答案都是正确的,但他的更加正确。 - memmons
2
@Answerbot,你的评论是不正确的,并且忽略了我在答案的最后一段所提出的观点。你可能会或可能不会关心对象类型,这取决于情况。如果你想要将在UIViewController中声明的消息发送给我答案中的vc,而它被声明为id<MyProtocol>,那么会发生什么? - Nick Forge
不确定我的评论哪里有误?无论如何,如果您正在检查对象是否符合协议,为什么要调用与协议无关的其他方法呢?我无法回忆起自己曾经需要这样做或在审查的代码中看到过这种情况。对我来说似乎是一种代码异味。 - memmons
仅仅因为你没有看到或使用过它,并不意味着它就是代码异味。这里有一个代码片段,展示了使用 id 抛弃类型信息会带来问题的一个例子:https://gist.github.com/nsforge/7743616 - Nick Forge

65

您可以这样转换它:

if([self.myViewController conformsToProtocol:@protocol(MyProtocol)])
{
    id<MyProtocol> p = (id<MyProtocol>)self.myViewController;
    [p protocolMethod];
}

这让我也有点困惑。在Objective-C中,协议本身不是类型,因此您需要在所需的协议旁指定id(或其他类型,例如NSObject)。


啊,好酷,谢谢。我刚刚检查了一下,发现将它强制转换为(id)也可以。那样做是否不规范? - Ford
1
如果你将它转换为id<MyProtocol>,那么编译器会在你使用未在该协议中定义的方法时发出警告。 - dreamlax
1
@dreamlax - 这就是编译器如何针对协议进行类型检查的方法。请参阅http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/chapter_7_section_11.html#//apple_ref/doc/uid/TP30001163-CH15-TPXREF151以获取更多信息。 - Andy
1
@Ford - 最好使用特定的协议,这样编译器可以为您执行一些类型检查。 - Andy
1
@Andy,我认为你不需要 '*',因为 'id' 已经是一个指针了。所以可以这样写:id p = (id)self.myViewController; [p protocolMethod];或者直接这样写:[(id)self.myViewController protocolMethod]; - Ford
有人知道为什么在子类化的 UICollectionView 上调用 if([self conformsToProtocol:@protocol(MyProtocol)]) 会让我陷入无限循环吗? - nambatee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接