对于objective-c来说很新,但我遇到了一个似乎很常见的情况:我希望ClassA要求ClassB对只有ClassB知道的对象执行一个方法(并且还使用一个方法ClassA不知道。
我发现了两种方法: performSelector:和 forwardInvocation: - 但我想了解更多并巩固我对每个方面的理解。我在苹果开发者文档中找到了这个注释:
aSelector参数[在performSelector:]中应该标识一个不需要的方法 参数。对于返回除对象以外的任何内容的方法,请使用 NSInvocation的。
..这是否意味着以 - (id)methodName 开头的方法将使用 performSelector:,而说 - (int)nonObjectMethodName 会使用 forwardInvocation:?
还有返回(void)的方法呢?或者返回非id对象的方法,例如(的NSString)
答案 0 :(得分:6)
没有。 -forwardInvocation:
用作消息转发机制的一部分。不要担心邮件转发,因为它实际上只被代理对象使用,并且赔率很高,你永远不需要使用它并知道你正在使用它。
-performSelector:
假定消息的返回类型为id
或兼容,因此如果用于发送返回类型不同的消息(例如,比{等指针宽),则不安全{1}}在32位系统上,或通过不同的寄存器/地址返回,例如long long
或大float
。)
如果您想间接发送这样的消息,您可以创建 struct
类的实例,然后发送它NSInvocation
。然后将返回值存储在调用对象中,并可通过它访问。在这种情况下,您永远不会使用-invoke
。
一般来说,如果你发现自己使用的是-forwardInvocation:
,那么你可能正在处理反模式。在这种情况下,您尝试发送-performSelector:
未正式了解的消息。另一种解决方案是公开这些私有方法。
如果您同时拥有ClassA
和ClassA
,则可以为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
使用它,但如果我没记错的话,我们在分布式对象之前就已经使用了它。这是实现“这个类无法处理的所有东西”的委托的一种方法。