Typically when you want to invoke methods on a separate thread, with a delay, back on the main thread after a long operation, etc, you are stuck with the performSelector:onThread: methods. With performSelector you are limited to invoking with a limited number of arguments, which must be objects; it won’t auto-unbox NSValue/NSNumber/NSNull like key value coding does.
This limitation can be really frustrating, particularly when you are using NSOperation or threading in general and need your delegates to call back onto the main (UI) thread.
A possible solution is to use NSInvocation with performSelector to deal with multiple arguments and primitives, for example:
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(listWithOffset:limit:)]];
NSInteger offset = 40;
[theInvocation setArgument:&offset atIndex:2];
NSInteger limit = 20;
[theInvocation setArgument:&limit atIndex:3];
[invocation performSelector:@selector(invoke) onThread:thread withObject:nil waitUntilDone:NO];
but it gets cumbersome, especially setting up the invocation instance. (BTW, the first argument is at index 2 because of the hidden arguments self and _cmd.) Also you would need to call retainArguments if your arguments were objects, that may be released by the calling thread.
Thankfully there is a better way. In a post called ‘Grab that Invocation‘ and later at Dave Dribbin’s post ‘Invoke on Main Thread‘ we can see how to get an NSInvocation instance from an NSProxy/forwardInvocation: automatically; and now we can combine the proxy with performSelector:onThread: or any of the other peformSelector methods to invoke back on the main thread, invoke on other threads, delay invocation, or other more general aspects like timing methods, debugging, logging, or security.
You can find Dave Dribbins original DDInvocationGrabber implementation in his DDFoundation library.
I’ve expanded on it a bit in GHKit, (see GHNSInvocationProxy) and plan on adding more features. If you use it with the GHNSObject+Invocation category, it gets even better:
// Invoke after 2 second delay (useful for simulating slow operations)
[[self gh_proxyAfterDelay:2.0] listWithOffset:40 limit:20];
// Invoke on main thread (if you ran on a separate thread or NSOperation
// and wanted to call back the delegate on the main thread)
[self listWithOffset:40 limit:20 delegate:[delegate_ gh_proxyOnMainThread:YES]];
// Later, after listing, the connection invokes the delegate back on the
// main thread
[delegate_ connection:self didListWithOffset:40 limit:20];
// Invoke on thread
[[self gh_proxyOnThread:thread waitUntilDone:NO] listWithOffset:40 limit:20];
// Time the invocation
[[self gh_timedProxy:&time] listWithOffset:40 limit:20];
NSLog(@"Took %0.2fs", time);
This category also includes other performSelector helpers (supporting var args and argument lists).
What might be some other ways to use this? Maybe an NSOperationProxy that allows you to queue and prioritize invocations, or more complex debugging or analytics proxies that can keep stats of certain activities.