转发方法调用Objective-C中具有不同签名的方法?

时间:2010-02-19 17:51:30

标签: objective-c class dynamic arguments selector

我正在尝试使用服务器连接器对象来实现JSON-RPC解决方案,该服务器连接器对象以某种方式从服务器获取可用功能列表

NSDictionary *functions = [server  
    callJSONFunction: @"exposedFunctions" arguments: nil];

这是一个简化的描述,因为callJSONFunction实际上触发了异步NSURLConnection。

函数列表的一个元素包括一个描述目标c选择器的字符串,将使用上述机制调用的原始函数名,函数签名和一个可选的参数名称数组。

因此,例如,函数列表可能如下所示:

( 
    @"someFunctionWithArgumentOne:argumentTwo:" =  
    {  
        signature = @"@@:@@",  
        functionName = @"someFunction",  
        arguments = ( @"arg_one", @"arg_two" )  
    },  
    @"anotherFunction" =  
    {  
        signature = @"@@:",  
        functionName = @"anotherFunction"  
    }  
)

成功检索功能列表后,选择器将在循环中使用class_addMethod添加到服务器连接器实例中:

for ( NSString *selectorName in functions ) {  
    SEL aSelector = NSSelectorFromString ( selName );  
    IMP methodIMP = class_getMethodImplementation ( 
        [ self class ], @selector ( callerMethod: ) );
    class_addMethod ( [ self class ], aSelector, methodIMP, "v@:@@@@" );
}

其中callerMethod:是用于组成实际请求的包装函数,包括作为NSString的函数名和形式为

的NSDictionary
{ @"argument1_name" = arg1, @"argument2_name" = arg2, ... }

因此签名为“v @:@@”。然后callerMethod在服务器上调用callJSONFunction

经过这段精疲力尽的介绍(我的不好,我只是不知道,如何缩短它)我终于明白了:要涵盖不同数量论点的可能性, 我将calllerMethod定义为
- (void) callerMethod: (id)argument, ... { }
其中我使用stdarg.h中的va_ *宏来获取传递的参数。但是当我通过调用

测试机制时
[serverConnector someFunctionWithArgumentOne: @"Argument 1"  
    argumentTwo: @"Argument 2" ];

id arg = va_arg ( list, id);返回的第一个参数始终为@"Argument 2"

我真的很感激为什么会发生这种情况的所有理论和解释。这件事真让我疯了!

2 个答案:

答案 0 :(得分:15)

Var-args不会映射到常规参数传递非常整齐。参数的编码实际上是特定于体系结构的,并且充满了非常有趣的细节,有时看起来它们是自相矛盾的(直到你发现关于规则的一个例外的注释,使整个事物连贯一致)。如果你真的想要一个好的阅读[讽刺意图],那么去看看ppc64如何处理长双打;有些情况下,double的一半将在寄存器中,另一半在堆栈中。 Whee!

上面很长,由于疤痕而略带泡沫,段落就是说你不能透明地将一个函数的调用转发到另一个函数,其中两个函数采用不同的参数。由于Objective-C方法实际上只是一个函数,因此方法也是如此。

相反,请使用NSInvocation,因为它旨在隐藏包含任何给定平台ABI的参数编码的所有深奥细节。

但是,在您的情况下,您可以通过定义一组定义所有可能的论证组合的函数来逃避class_addMethod()。您甚至不需要创建字典,因为您可以使用dlsym()函数来查找正确的函数。即。

id trampolineForIdIdSELIdIdInt(id self, SEL _cmd, id obj1, id obj2, int) {
    ... your magic here ...
}

然后,您可以将类型字符串“@@:@@ i”翻译成该函数名称并将其传递给dlsym,获取结果并使用class_addMethod() ....

我确实有义务提及this book,因为它是一种“哇......人......如果我们将类表示为称为元类的对象,那么它们本身就可以表示为类,比如,将宇宙重新定义为元类和类“这一思路的终极目标。”

答案 1 :(得分:1)

另见Gregor Kiczales和Andreas Paepcke的unfinished book