Many UI frameworks, including AppKit for Mac OS X and UIKit for iPhone OS, require that all methods to UI components are sent on the main UI thread. Cocoa and Cocoa Touch make this quite easy by providing for example
-[NSObject performSelectorOnMainThread:withObject:waitUntilDone:] in Foundation. Making updating the text for a text field a snap:
withObject:@"A new text"
Since Objective-C is a superset of C, there are still all the types from C available. Such as all the primitives, including enums, and more complex struct types. Cocoa Touch is pragmatic and will use the most proper type for the use-case, not forcing everything into a square OOP hole.
This makes it quite hard to call for example
-[UIView setHidden:], that takes a single
BOOL argument. Same for
-[UIView setFrame:], that takes a single
CGRect struct argument, that in turn consists of one
CGPoint and one
Every type imaginable in C can be bundled in an
NSValue instance. So we could bundle up the
BOOL primitive, or the
CGRect struct in a
NSValue object. Then pass that object to the main thread, where it is unbundled and passed to the desired method. Quite cumbersome as it requires us to bundle, and unbundle types manually, and implement at least one extra method.
It turns out that
NSInvocation can also handle any imaginable C type, or else it would not be able to invoke any imaginable Objective-C method. Making a
NSInvocation invoke its method on the main thread is easy enough. Just add a category to the
NSInvocation class, with a new method like this:
So now all you need to do is to create a
NSInvocation object, fill it in with the primitive or struct types, and invoke it on the main thread. But creating and setting up a
NSInvocation object is also a bit on the boring side…
We could use variable argument lists from plain old C. Too bad that they are untyped?
No despair, we know the target object for our invocation, and we know what method to call. So we have what we need to fetch the
NSMethodSignature object for the call, that contains all the type information we need to safely process the
Our target machine is a Mac running 32- or 64-bit Intel CPU, or an iPhone OS device with a 32 bit ARM CPU. Turns out that on both platforms
va_list is simply a
void* pointer, to the stack frame. Even better
va_start() will always flush any argument passed into the register on the stack frame. So we can skip most of the boring argument handling, by treating the arguments like a byte buffer, only advancing and aligning the buffer according to the information in the
A convenience method for creating a
NSInvocation object for a particular target, selector, and list of variable arguments, would turn out like this: