Objective-C:performSelector:vs forwardInvocation:

时间:2012-07-07 23:38:29

标签: objective-c ios ios5 ios5.1

对于objective-c来说很新,但我遇到了一个似乎很常见的情况:我希望ClassA要求ClassB对只有ClassB知道的对象执行一个方法(并且还使用一个方法ClassA不知道。

我发现了两种方法: performSelector: forwardInvocation: - 但我想了解更多并巩固我对每个方面的理解。我在苹果开发者文档中找到了这个注释:

  

aSelector参数[在performSelector:]中应该标识一个不需要的方法   参数。对于返回除对象以外的任何内容的方法,请使用   NSInvocation的。

..这是否意味着以 - (id)methodName 开头的方法将使用 performSelector:,而说 - (int)nonObjectMethodName 会使用 forwardInvocation:

还有返回(void)的方法呢?或者返回非id对象的方法,例如(的NSString)

3 个答案:

答案 0 :(得分:6)

没有。 -forwardInvocation:用作消息转发机制的一部分。不要担心邮件转发,因为它实际上只被代理对象使用,并且赔率很高,你永远不需要使用它并知道你正在使用它。

-performSelector:假定消息的返回类型为id或兼容,因此如果用于发送返回类型不同的消息(例如,比{等指针宽),则不安全{1}}在32位系统上,或通过不同的寄存器/地址返回,例如long long或大float。)

如果您想间接发送这样的消息,您可以创建 struct类的实例,然后发送它NSInvocation。然后将返回值存储在调用对象中,并可通过它访问。在这种情况下,您永远不会使用-invoke

一般来说,如果你发现自己使用的是-forwardInvocation:,那么你可能正在处理反模式。在这种情况下,您尝试发送-performSelector:未正式了解的消息。另一种解决方案是公开这些私有方法。


如果您同时拥有ClassAClassA,则可以为ClassB创建一个“私有”标头,其中包含您要使用的私有方法。如果其他人(例如Apple)拥有ClassB,您正在处理未记录的API,并且可能需要寻找其他方法,因为Apple将拒绝使用此类API的应用程序。

要创建私有标头,请进入Xcode并创建新的标头文件。将其命名为“ClassB + Internal.h”或“ClassB + PrivateMethodsForMeOnly.h”。将其视为您项目的私有 - 除非他们是ClassB的同行(相同的子项目或库或组件),否则没有人可以使用它。在此新标头中,添加以下内容:

ClassB

并在#import "ClassB.h" // so we get the original class definition @interface ClassB (PrivateMethodsForMeOnly) - (double)someMethod; - (const struct low_level_c_type_t)otherMethod:(int)i; // etc. etc. etc. @end ClassA.m,除非您希望将这些方法公开给使用ClassA.h的所有人!)在您的包含部分添加以下行:

ClassA
此后

#import "ClassB+PrivateMethodsForMeOnly.h" 可以访问新类别中的这些方法。

答案 1 :(得分:0)

好的,这是几个问题。

首先,忘记" forwardInvocation" - 这几乎是一种只需要制作代理对象的方法。

使用[NSInvocation invocationWithMethodSignature ...]创建调用,然后使用选择器,参数和目标对象填充调用,最后您只需调用"它。这就是你如何发送(甚至存储)任何方法调用。

performselector存在于多个变体中,其中一些变量采用参数,在延迟后调用它,或者在另一个线程上运行该方法。它更直接" call - NSInvocation是一个对象,你可以像任何其他对象一样保持它,直到你需要它,而" performselector"几乎可以立即运行该方法。

关于返回值,我通常坚持文档所说的内容;如果它告诉我该方法应该返回一个id我返回一个对象(任何对象指针都没问题,所以NSString *很好。返回nil也没关系,这几乎涵盖了' void' case),而不是void而不是int。

另外,请记住,调用performselector并不是常见的;在大多数情况下,您只需按照" [MyObject SomeMethod:SomeParameter];"进行硬编码。调用performSelector样式的方法适用于特殊情况 - GUI对象上的目标/操作内容就是这样一种情况,其中要调用的选择器存储在对象中,因此显然无法对其进行硬编码。

答案 2 :(得分:0)

-forwardInvocation:的目的是让一个对象尝试发送一条消息,它不会识别其他对象而不是放弃。 NSInvocation不仅包含选择器,还包含消息的所有参数。 NSProxy使用它,但如果我没记错的话,我们在分布式对象之前就已经使用了它。这是实现“这个类无法处理的所有东西”的委托的一种方法。

您应该阅读section on message forwarding in the Objective-C docs.