ConformsToProtocol: && RespondsToSelector: 与 respondsToSelector: 的区别

7

当想要在代理对象上调用协议方法时,希望该代理对象实现相应的协议方法。我看到有些开发人员首先检查

if([delegate respondsToSelector: @selector(aMethod)])
    {
        //send message;
    }

这样做不是更好,甚至更安全吗?
if([delegate conformsToProtocol:@protocol(MyProtocol)] && [delegate respondsToSelector: @selector(aMethod)])
    {
        //send message;
    }

我知道,如果协议方法的定义已经被正确地组织,那么代理中就不会出现任何冲突或实现,这些冲突可能并非来自 MyProtocol 或者是没有意图的。这样的冲突很遥远,但我曾经遇到过一个协议方法定义,仅声明为 -(void)willStartLogin;。我相信你已经开始思考和建议这样的协议方法有多糟糕了,例如,它可能已经被代理用于个人或内部使用,而不是在 myDelegate 协议下使用。更好的做法是将 MyProtocol 的方法声明为:-(void)myObjectWillStartLogin:(MyObject*)myObjectInstance; 这样可以消除任何歧义,使事情变得明显。
我希望我没有漏掉任何需要检查 respondsToSelector: 的必要性的东西:
谢谢

1
虽然你说得没错,有时候会不小心调用一个不适用于当前委托模式的方法,但甚至一些Cocoa类也没有检查“符合”和“响应”两个方面。这完全取决于你想要多么谨慎。就我个人而言,我通常还有其他问题需要解决。 - Hot Licks
我认为大多数库只使用 respondsToSelector:conformsToProtocol: 应该在编译时检查。 - Bryan Chen
我相信Cocoa和Cocoa Touch框架从不检查协议的一致性,只响应选择器。 - newacct
这些框架有时会检查协议的一致性。 - Greg Parker
2个回答

7
我不太确定你在问什么,但也许这篇文章可以帮到你:
协议是一系列方法的集合,其中有些方法是必需的,而有些方法则是可选的。而“conformsToProtocol:”所回答的问题是一个对象是否声称实现了一堆方法(那些必需的方法),并且可能还会实现其他一些方法(那些可选的方法)。需要注意的是,这里是 “声称实现了”,而不是 “实际上实现了”,因为未能实现必需的方法并不会防止编译(只会产生一个警告)。
而“respondsToSelector:”所回答的问题是一个对象是否实现了特定的方法。与“conformsToProtocol:”不同,它为特定方法提供了明确的答案。
正是由于其明确性,“respondsToSelector:”才被广泛使用。
你可能认为检查方法的同时也检查协议更好,因为它意味着这个方法做你预期的事情的可能性更大。如果是这样的话,同时使用“respondsToSelector:”和“conformsToProtocol:”可以告诉你你想要的答案……有点像 - 因为协议只是方法名和签名,这些方法的行为是暗示而不是强制性的(*)。
希望对你有帮助。 (*如果你想要强制性的合同,请看Eiffel等语言)

谢谢你的回答。我知道conformsToProtocol不会检查协议方法是否已经实现。但我的问题是:在使用respondsToSelector之上,是否检查目标是否响应选择器,因为它符合协议。它可能会响应,因为它符合另一个具有完全相同方法的协议,或者只是巧合。当然,这种情况非常不可能,因为如果[delegate RTS:sel]=YES,这意味着我们已经执行了例如:engine.delegate = self; 这意味着我们想要从引擎接收消息。希望我的意思表达清楚。 - pnizzle

4
如果一个对象必须完全符合某个协议,那么应该按照以下方式声明对象:

id<MyProtocol> delegate;

这将会在编译时为任何试图将不符合“MyProtocol”协议的对象分配给变量/参数/属性“delegate”的人生成编译时错误。(他们可以使用显式转换来避免警告,但是需要他们明智地这样做。) 你仍然需要检查“respondsToSelector”,以防万一“delegate”将会响应选择器,因为有很多方式它可能不会。(致谢:@Hot Licks)

而且将错误的对象类型分配给指针是绝对不可能的,对吧? - Hot Licks
@HotLicks 您说得对,为了防止可能发生的运行时异常,仍然需要检查 respondsToSelector - godel9
@godel9 很好。我最终在午餐时想起,如果委托属性不为nil,则意味着我们在某个地方设置了委托,这意味着我们希望接收协议声明的消息/方法调用。正如您所指出的那样,如果被设置为委托的对象不符合协议,则会出现警告。所以我猜respondsToSelector就可以解决问题,尽管检查两者也不会影响任何逻辑。 - pnizzle
@godel9,您能否举出除运行时问题以外的委托可能不响应选择器的其他方式的示例? - allenlinli
这是一个很好的“非正式协议”的例子: https://dev59.com/13I-5IYBdhLWcg3wHUdB - allenlinli

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