问题
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