基于索引的Objective-C参数值的提取

时间:2013-02-01 11:49:25

标签: objective-c nsinvocation

我想使用正确的参数值为当前方法动态创建NSInvocation。通常,人们可能会这样做:

- (void)messageWithArg:(NSString *)arg arg2:(NSString *)arg2
{
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:_cmd]];
    [invocation setTarget:self];

    /*
     * How do we set the argument values here dynamically?
     */
}

显式设置参数值是微不足道的,我们可以这样做:

[invocation setArgument:&arg atIndex:2];
[invocation setArgument:&arg2 atIndex:3];

我希望能够在循环中执行此操作:

for(int i = 0; i < [[self methodSignatureForSelector:_cmd] numberOfArguments]; i++) {
    [invocation setArgument:?!?! atIndex:i + 2];
}

困难的部分是动态获取给定索引的参数值。

一个类似的问题被问到here,回答者说他并不知道一个解决方案的复杂性。我不同意复杂性 - 在底层代码中我们已经确切知道堆栈框架设置后应该如何看待堆栈,因为编译器知道所使用的调用约定。例如,在带有stdcall的x86上,我们可以轻松访问参数值,因为我们知道它们是ebp的固定偏移量:

  • ebp位于 0(%ebp)
  • 4(%ebp)
  • 的返回地址
  • 第一个参数 8(%ebp)

如何实现我想要的功能或者语言中是否存在任何支持基于索引的参数值提取的机制?在这一点上,我可以接受这一点,因为C标准中不存在这样的特征。但是,我想得到确认和/或解释背后的原因。

1 个答案:

答案 0 :(得分:0)

这有效,但不是我的预期。 va_start used in function with fixed arguments错误阻止我在普通方法中使用va_start。取决于你想要实现的目标可能是有用的。

@interface Test : NSObject

- (void)method:(id)arg1 :(id)arg2;

@end

@implementation Test

+ (void)load {
    class_addMethod([Test class], @selector(method::), (IMP)method_imp, "v@:@@");
}

void method_imp(id self, SEL _cmd, ...) {
    va_list ap;
    va_start(ap, _cmd);
    SEL sel = NSSelectorFromString([@"_" stringByAppendingString:NSStringFromSelector(_cmd)]);
    NSMethodSignature *signature = [self methodSignatureForSelector:sel];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    int argc = [signature numberOfArguments];
    char *ptr = (char *)ap;
    for (int i = 2; i < argc; i++) {
        const char *type = [signature getArgumentTypeAtIndex:i];
        [invocation setArgument:ptr atIndex:i];
        NSUInteger size;
        NSGetSizeAndAlignment(type, &size, NULL);
        ptr += size;
    }
    va_end(ap);
    [invocation setSelector:sel];
    [invocation invokeWithTarget:self];
}

- (void)_method:(id)arg1 :(id)arg2 {
    NSLog(@"%@, %@, %@", NSStringFromSelector(_cmd), arg1, arg2);
}

@end

来电method::将以_method::结尾,并且没有任何硬编码

Test *test = [[Test alloc] init];
[test method:@"arg1" :@"arg2"];  // log: _method::, arg1, arg2