Index-based fetching of Objective-C argument values

匆匆过客 提交于 2019-12-24 05:27:08

问题


I want to dynamically create the NSInvocation for the current method with the correct argument values. Typically, one might do this:

- (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?
     */
}

Setting the argument values explicitly is trivial and we could do something like this:

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

I want to be able to do this in a loop with something like this:

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

The difficult part is dynamically getting the argument values for a given index.

A similar question was asked here where the answerer says he is not aware of a solution citing the complexity of the class. I disagree regarding the complexity - in the underlying code we already know exactly how the stack should look after the stack frame is set up because the compiler is aware of the calling convention used. For example, on x86 with stdcall we could easily access the argument values because we know they are a fixed offset from ebp:

  • Old ebp at 0(%ebp)
  • Return address at 4(%ebp)
  • First argument at 8(%ebp)
  • etc.

How can I achieve what I want or does there really not exist any mechanism in the language to support index-based fetching of argument values? At this point, I could accept this as true because no such feature exists in the C standard. However, I would like to get confirmation and/or an explanation of the reasoning behind this.


回答1:


this works, but not what I was expected. va_start used in function with fixed arguments error stops me to use va_start in normal method. depends what you trying to achieve this may be useful.

@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

call method:: will end up with _method:: and nothing is hardcoded

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


来源:https://stackoverflow.com/questions/14645659/index-based-fetching-of-objective-c-argument-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!