Objective-C中的performSelector:和forwardInvocation:有什么区别?涉及到IT技术。

3

我对Objective-C还比较陌生,但遇到了一个比较常见的情况:我想让ClassA请求ClassB在只有ClassB知道的对象上执行一个方法(同时使用一个ClassA不知道的方法)。

我找到了两种做法:performSelector:forwardInvocation:,但我想学习更多,并巩固我对每个方法的理解。我在苹果开发者文档中找到了以下提示:

[performSelector:]的aSelector参数应该标识一个不带参数的方法。对于返回任何非对象的方法,请使用NSInvocation。

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

还有那些返回(void)或返回非id对象,例如(NSString)的方法怎么办?


请注意,还存在-performSelector:withObject:。 - NSResponder
3个回答

7

-forwardInvocation:是消息转发机制的一部分。不必担心消息转发,因为它只被代理对象使用,在通常情况下你永远不需要使用它或知道你正在使用它。

-performSelector:假设消息的返回类型是id或兼容类型,因此如果用它来发送返回类型不同的消息(例如32位系统上比指针更宽的long long,或通过不同的寄存器/地址返回的float或大型struct),就不安全了。

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

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


如果你拥有ClassAClassB,可以为ClassB创建一个“私有”头文件,其中包含你想要使用的私有方法。如果其他人(例如苹果)拥有ClassB,那么你正在处理未记录的API,并且可能需要寻找另一种方法,因为苹果将拒绝使用这些API的应用程序。

要创建私有头文件,请进入Xcode并创建一个新的头文件。将其命名为“ClassB+Internal.h”或“ClassB+PrivateMethodsForMeOnly.h”之类的名称。将其视为私有于你的项目-除了与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的人都看到这些方法!)在你的include部分添加以下行:
#import "ClassB+PrivateMethodsForMeOnly.h"

ClassA 将随后可以访问该新类别中的这些方法。


私有头文件的使用在构建框架或库时是一件美妙的事情。在单个项目中,这样做可能有些愚蠢,因为所有类都可以相互访问。但是,强制执行这种严格性会迫使您更多地考虑类层次结构,这从来不是浪费时间的事情。 - Jonathan Grynspan
我确实拥有这两个类 - 但这个问题的主要原因真正在于追求正确的MVC或MVC+Store架构 - 在我的情况下,ClassA是一个自定义的UITableViewCell类,被UITableViewController类(ClassB)'调用'。被操作的对象是一个简单的NSObject类 - 但在我的应用程序中,只有ClassB可以访问该NSObject类,而我想保持这种状态... - toblerpwn

0

好的,这是几个问题。

首先,忘掉"forwardInvocation"——那基本上是一个你只需要创建代理对象的方法。

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

performselector存在多种变体,其中一些带有参数,在延迟后调用它,或在另一个线程上运行该方法。这是一种更"直接"的调用——NSInvocation是一个对象,你可以像任何其他对象一样保留它,直到你需要它,而"performselector"基本上立即运行该方法。

至于返回值,我通常遵循文档所说的;如果它告诉我该方法应返回一个id,我就返回一个对象(任何对象指针都可以,所以NSString*也可以。返回nil也可以,这基本上涵盖了'void'情况),不是void也不是int。

此外,请记住,调用performselector并不是非常常见的;在大多数情况下,您只需硬编码调用,例如"[MyObject SomeMethod:SomeParameter];"。调用performSelector风格的方法是为特殊情况准备的——GUI对象上的目标/操作内容就是这样一个例子,其中要调用的选择器存储在对象中,因此显然无法硬编码。

0

-forwardInvocation: 的目的是让一个对象尝试把它不能识别的消息发送给另一个对象,而不是直接放弃。NSInvocation 不仅包含选择器,还包含消息的所有参数。NSProxy 使用它,但如果我记得正确的话,我们在分布式对象之前就有它了。这是一种实现“所有这个类无法处理的事情”的委托的方法之一。

你应该阅读Objective-C 文档中有关消息转发的部分。


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