2

我想用正确的参数值为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];
}

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

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

  • ebp0(%ebp)
  • 返回地址为4(%ebp)
  • 第一个参数8(%ebp)
  • 等等

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

4

1 回答 1

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
于 2013-02-01T12:28:54.907 回答