I recently ran into the situation where I wanted to call NSObject’s performSelectorOnMainThread with multiple arguments. While it wasn’t massively difficult, it was a bit more fiddly than I first expected - you have to get your hands dirty with NSInvocation. I’ve built a category that adds an additional method to NSObject which will allow you to call a performSelectorOnMainThread method with any number of objects as parameters. Feel free to use it in your own projects!

If you’re new to Objective-C, categories allows you to add additional methods to existing classes - without having to have access to the source code for that class. In this instance, we will add a new method to NSObject (and therefore, every object that inherits from NSObject) that will act much like the already existing performSelectorOnMainThread method, only it will accept any number of objects as input. You can read more about categories in this very helpful Objective-C guide.

Our code consists of two parts, the header file - which will define the extra method for NSObject, and the .m file, as per usual. The header file is simple (excuse my long file name, I like to be precise);

NSObject+PerformSelectorOnMainThreadMultipleArgs.h

#import <Foundation/Foundation.h>

@interface NSObject (PerformSelectorOnMainThreadMultipleArgs)

-(void)performSelectorOnMainThread:(SEL)selector waitUntilDone:(BOOL)wait withObjects:(NSObject *)object, ... NS_REQUIRES_NIL_TERMINATION;

@end

Nothing really out of the ordinary here. Just notice that using the (parathesis) after NSObject tells Objective-C that we’re making a category on NSObject. It’s also worth noting that the ‘with objects’ parameter for the method we’ve created has a comma and 3 ellipsis after the first parameter’s variable name. This is stating that we’re expecting a list of parameters. The NS_REQUIRES_NIL_TERMINATION states that, much like some of the NSMutableArray instance methods, we’re expecting a list which is terminated by a nil value. Now, onto the code for this method;

NSObject+PerformSelectorOnMainThreadMultipleArgs.m

#import "NSObject+PerformSelectorOnMainThreadMultipleArgs.h"

@implementation NSObject (PerformSelectorOnMainThreadMultipleArgs)

-(void)performSelectorOnMainThread:(SEL)selector waitUntilDone:(BOOL)wait withObjects:(NSObject *)firstObject, ... 
{
    // First attempt to create the method signature with the provided selector.
    NSMethodSignature *signature = [self methodSignatureForSelector:selector];
	
    if ( !signature ) {
        NSLog(@"NSObject: Method signature could not be created.");
        return;
    }

    // Next we create the invocation that will actually call the required selector.
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    [invocation setTarget:self];
    [invocation setSelector:selector];

    // Now add arguments from the variable list of objects (nil terminated).
    va_list args;
    va_start(args, firstObject);
    int nextArgIndex = 2;
	
    for (NSObject *object = firstObject; object != nil; object = va_arg(args, NSObject*))
    {
        if ( object != [NSNull null] ) 
        {
            [invocation setArgument:&object atIndex:nextArgIndex];
        } 
	
        nextArgIndex++;
    }	
	
    va_end(args);
	
    [invocation retainArguments];
    [invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:wait];
}

@end

Now, you probably came here just looking for some code - so I won’t put too much tutorial in here. First off, let me just say that I’ve switched around the parameters for the method from the standard order so that the variable length list is the last parameter. Next up, I’ll just touch on the two main elements of the code.

First of all, this code relies on NSInvocation to do the leg work. NSInvocation represents the action of calling a method, but wraps it up in an object - so that you can do a number of things with it. In this case, it means that we can build the NSInvocation from the list of input objects that we’ve received, and then ensure that the NSInvocation object calls it’s own invoke method (calling the selector on the calling item) on the main thread. This means that we can lever the regular performSelectorOnMainThread functionality without having to do too much work on our own.

Secondly, the va_list is what allows us to pass the method a variable number of arguments. We keep popping objects off the argument list (in the for loop) until we reach a nil object. However, as we might also want to pass nil as an argument to our selector, I’ve made it such that you can pass NSNull which will then be interpreted as a nil argument in the final selector call. Here we simply pop the arguments from the withObjects parameter to the method and add them as arguments to the invocation. Note that we start at argument 2 for the invocation as NSInvocation’s arguments 0 and 1 are used for other purposes. So argument 2 actually corresponds to the first argument for the selector you’ve chosen.

That’s about it really - if you have any questions, please feel free to ask away in the comments section below. Otherwise, enjoy the code!