NSInvocation and NSMethodSignature and variable arguments

So what happens when you try to use NSInvocation and NSMethodSignature with methods that accept a variable number of arguments? This is a question that I was asking myself earlier this evening.

The results

Given a method:

- (void)testMethod1:(NSString *)something, ...

The following statement:

NSLog(@"%@", [TestClass instanceMethodSignatureForSelector:@selector(testMethod1:)]);

Prints (timestamp, process name and ID omitted for brevity):

NSMethodSignature: types=v@:@ nargs=3 sizeOfParams=160 returnValueLength=0;

This method: - (void)testMethod2:(NSString *)something otherThing:(id)thing

Prints:

NSMethodSignature: types=v@:@@ nargs=4 sizeOfParams=160 returnValueLength=0;

And this one: - (void)testMethod3:(NSString *)something

Prints:

NSMethodSignature: types=v@:@ nargs=3 sizeOfParams=160 returnValueLength=0;

Conclusions

Methods that take variable numbers of arguments appear no different to the runtime than methods which do not (note that testMethod1 and testMethod3 have the same signature). This means that you can’t use NSInvocation to represent invocations of methods which take multiple arguments (if you try to call setArgument:atIndex: for the additional arguments an exception will be raised). It also means that my mock object implementation in the WOTest framework won’t work properly with such methods (it simply won’t "see" the additional arguments). And finally, it means that I have to refactor some of my code (methods which take variable numbers of arguments) if I want to use it with NSInvocation (and I do).

What kind of refactoring? Basically you have to do the same as Apple has done with functions like NSLog and NSLogv. One takes a variable number of arguments and the other takes a va_list as its last parameter. To avoid code duplication you can have the first form prepare the va_list and invoke the other.

And yes, the fact that NSInvocation does not support methods with a variable number of arguments is noted in the documentation:

NSInvocation does not support invocations of methods with either variable numbers of arguments or union arguments.

But I didn’t notice that until after I’d scaled the little learning curve described in this article.