【问题标题】:iOS Message Forwarding methodSignatureForSelectoriOS消息转发方法SignatureForSelector
【发布时间】:2015-07-27 06:56:26
【问题描述】:

所以我正在寻找into message forwarding 并运行一些单元测试,因为我遇到了一些稀缺的资源,包括苹果自己品牌的使用forwardInvocation: 的文档,据我所知需要methodSignatureForSelector: 才能工作。

现在我知道methodSignatureForSelector: 需要查看您尝试将消息转发到的对象是否具有匹配的方法名称和参数,以便它可以调用forwardInvocation: 我的问题是为什么在苹果文档中它说像这样调用methodSignatureForSelector: 的超类实现......

- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];
    if (!signature) {
        signature = [surrogate methodSignatureForSelector:selector];
    }
    return signature;
}

在我看来,这似乎是在说“如果我继承的所有类都没有处理此方法的方法,请检查代理对象是否有。”

在谈判方法方面,苹果给出的例子是勇士代替外交官行事。鉴于这个例子,我不明白你为什么要检查战士或其任何父母是否有适当的方法签名来转发。所以这让我相信它的存在是出于另一个原因,一个我想不出来的原因,有人可以给我一个例子或帮助澄清我可能遗漏的地方吗?

TL;DR 为什么我需要[super methodSignatureForSelector:selector];

【问题讨论】:

    标签: ios objective-c method-signature message-forwarding


    【解决方案1】:

    你是对的——这个例子试图从超类中获取方法签名,如果不能,它会向代理请求一个。在整个转发和继承部分,Apple 正在指导您如何使用消息转发 - 您是否选择收听取决于您:)

    为了全面解释,我将介绍文档的主要部分:

    1. 虽然转发模仿继承,但 NSObject 类从不混淆两者。 respondsToSelector: 和 isKindOfClass: 等方法只查看继承层次结构,从不查看转发链。

    实现消息转发不会立即影响respondsToSelectorisKindOfClass 方法。如果您将negotiate 转发给代理,如果您调用[myWarrior respondsToSelector:negotiate],如果该方法在继承层次结构中不存在,则将返回NO

    1. 如果您使用转发来设置代理对象或扩展类的功能,则转发机制应该可能像继承一样透明。如果您希望您的对象表现得好像它们真正继承了它们将消息转发到的对象的行为,您需要重新实现 respondsToSelector: 和 isKindOfClass: 方法以包含您的转发算法。

    关键字可能是 - 所以 Apple 给你一个建议。 Apple 声明如果您希望 myWarrior 对象在上面的示例中返回 YES,因为您将 negotiate 转发到代理对象,那么您需要覆盖 respondsToSelector 方法。现在请注意,除了您可以调用的negotiate 之外还有其他方法,可能在您不希望返回YES 的代理中。例如, Diplomat 类可能有一个havePeaceCelebration 方法。当 Warrior 类收到此消息时,您可能尚未将消息转发给 Diplomat 类(因为 Warriors 没有和平庆祝活动),因此您需要返回 NO

    此外,父类可能有一个不在 Warrior 类中的 chooseWeapon 方法。如果你打电话给[myWarrior respondsToSelector:chooseWeapon],你肯定想检查超类是否响应它,因为代理(作为外交官)没有。

    最后,父类和代理都可能响应选择器。 Apple 似乎建议父类应该胜出——战士首先是战士,而对于某些方法来说是外交官,除非你强迫它这样做。不过,您最终如何实现它取决于您。

    1. 除了 respondsToSelector: 和 isKindOfClass:, instanceRespondToSelector:方法应该也反映转发 算法。如果使用协议,conformsToProtocol: 方法 同样应该被添加到列表中。同样,如果一个对象转发 它收到的任何远程消息,它应该有一个版本 methodSignatureForSelector:可以返回准确的描述 最终响应转发消息的方法;为了 例如,如果一个对象能够将消息转发给它的代理, 您将实现 methodSignatureForSelector: 如下:

    关键字是应该 - 再次是推荐。这是您提供的代码之前的声明。这与respondsToSelector 的推理相同,并且说对象应该是一个好公民。 Warrior 类可以处理一些远程消息,但不能处理其他消息,对于 Diplomat 类也是如此。如果您选择始终将其转发给代理,如果转发的消息是 Warrior 的超类可以处理的,则可能会令人困惑。或者更糟的是,它可能会转发 Warrior 的超类可以处理但代理不能处理的消息 - 可能会导致异常。

    【讨论】:

    • 这是有道理的,我问的原因是因为我当前的类不是某种代理,而是包含多个可能对同一消息感兴趣的实例。然而,它本身对能够响应它将转发给代理人的消息没有兴趣。所以真的在这种特殊情况下,我认为我不需要检查超类是否可以首先实现这个方法。 P.S respondsToSelector:methodSignatureForSelector: 有什么区别?
    【解决方案2】:

    如果您向不处理该消息的对象发送消息,则在宣布错误之前,运行时会向该对象发送 forwardInvocation: 消息,其中 NSInvocation 对象作为其唯一参数 - NSInvocation 对象封装了原始消息和参数与它一起传递的。

    【讨论】: