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

kar*_*ten 1 arguments class dynamic objective-c selector

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

NSDictionary *functions = [server  
    callJSONFunction: @"exposedFunctions" arguments: nil];
Run Code Online (Sandbox Code Playgroud)

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

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

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

( 
    @"someFunctionWithArgumentOne:argumentTwo:" =  
    {  
        signature = @"@@:@@",  
        functionName = @"someFunction",  
        arguments = ( @"arg_one", @"arg_two" )  
    },  
    @"anotherFunction" =  
    {  
        signature = @"@@:",  
        functionName = @"anotherFunction"  
    }  
)
Run Code Online (Sandbox Code Playgroud)

成功检索功能列表后,选择器将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@:@@@@" );
}
Run Code Online (Sandbox Code Playgroud)

where callerMethod:用于组成实际请求的包装函数,包括作为NSString的函数名和表单的NSDictionary

{ @"argument1_name" = arg1, @"argument2_name" = arg2, ... }
Run Code Online (Sandbox Code Playgroud)

因此签名"v @:@@".然后callerMethod callJSONFunction在服务器上调用.

经过这个令人筋疲力尽的介绍(我的不好,我只是不知道,如何缩短它)我终于明白了:为了涵盖不同数量的参数的可能性,我定义了callerMethod就像
- (void) callerMethod: (id)argument, ... { }
我使用va_*宏从中stdarg.h获取传递的参数.但是当我通过调用测试机制时

[serverConnector someFunctionWithArgumentOne: @"Argument 1"  
    argumentTwo: @"Argument 2" ];
Run Code Online (Sandbox Code Playgroud)

返回的第一个参数id arg = va_arg ( list, id);总是@"Argument 2"!

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

bbu*_*bum 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 ...
}
Run Code Online (Sandbox Code Playgroud)

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

我确实觉得有义务也提到这本书,因为它是一种"哇......男人......如果我们将类表示为被称为元类的对象,它们本身就被表示为类,那么我们就可以将宇宙重新定义为元类和类"这一思路的终极目标.