Performing any Selector on the Main Thread

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:

But not everything is an object

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 CGSize struct.

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.

There must be an easier way

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…

There must be an even easier way!

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 va_list.

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 NSMethodSignature object.

A convenience method for creating a NSInvocation object for a particular target, selector, and list of variable arguments, would turn out like this:

Conclusion

And now we can easily call any method on the main UI thread.

An example where a CGRect struct is used to update the UI components frame;

A slightly more complex example, where we send a primitive int, and also waits for and fetches the result from the main thread:

Download full source code here: NSInvocation+CWVariableArguments.zip

Update: Example 2 was not 64-bit compatible as David Remahl pointed out to me. Added type cast to fix the error.

Update 2: A new version with some more features and small bug-fixes to BOOL, float and uint16_t types, plus proper unit tests can be downloaded here: CWFoundationAsyncAdditions.zip

34 Responses to “Performing any Selector on the Main Thread”

  1. I will recommend not to wait until you earn big sum of money to buy all you need! You can get the credit loans or college loan and feel comfortable

  2. DabJeannaWags says:

    Hallo liebe Leute!

    Ich möchte euch gerne eine Website von mir zeigen:
    http://beste-filme-heute.blogspot.com/

    Hier findet ihr eine Sammlung der besten Filme es gibt!

    Alle paar Tage kommen neue Filme hinzu.

    Ich danke euch für euren Besuch!

  3. mymoneymonitore says:

    Anyone who reads Old and Middle English my money monitor literary texts
    will be familiar with the mid-brown volumes of the EETS,
    with the symbol of Alfred’s jewel embossed on the front
    cover. Most of the works attributed to my money monitor King Alfred or to
    Aelfric, along with some of those by bishop Wulfstan and
    much anonymous prose and verse from the pre-Conquest period,
    are to be found within the Society’s three series my money monitor

  4. … [Trackback]…

    [...] Read More Infos here: jayway.com/2010/03/30/performing-any-selector-on-the-main-thread/ [...]…

  5. tatuaggi says:

    … [Trackback]…

    [...] Read More here: jayway.com/2010/03/30/performing-any-selector-on-the-main-thread/ [...]…

  6. Liza Eby says:

    … [Trackback]…

    [...] Informations on that Topic: jayway.com/2010/03/30/performing-any-selector-on-the-main-thread/ [...]…

  7. abullyskack says:

    Please delete, i’ve posted it in wrong topic.

  8. rebornbhms says:

    Search business sites, newspapers, associations and assembly vocation pages

  9. gurnee says:

    … [Trackback]…

    [...] Read More Infos here: jayway.com/2010/03/30/performing-any-selector-on-the-main-thread/ [...]…

  10. cool site says:

    … [Trackback]…

    [...] Informations on that Topic: jayway.com/2010/03/30/performing-any-selector-on-the-main-thread/ [...]…

  11. Homepage says:

    … [Trackback]…

    [...] There you will find 76398 more Infos: jayway.com/2010/03/30/performing-any-selector-on-the-main-thread/ [...]…

  12. bob says:

    This varargs stuff is very ugly. It would be much cleaner to use a proxy object that uses -forwardInvocation: to perform it on the main thread. You would call it by something like this: [[MainThreadForwardProxy proxyWithTarget:self retainArguments:YES] setFrame:CGRectMake(40, 40, 200, 100)]

    [MainThreadForwardProxy proxyWithTarget:self retainArguments:YES] returns an object that has -forwardInvocation: and -methodSignatureForSelector:. Its -methodSignatureForSelector: simply asks its target for the signature. Its -forwardInvocation doesn’t have to do any varargs monkey business because it already gets the properly set up invocation.

  13. hermsceds says:

    Wie funktioniert Diabetes in der Schwangerschaft Einfache Informationen finden Sie auf der für diabetiker Board.

    http://www.diabetesboard.ch/wp-content/uploads/2012/02/blutzuckertest.jpg
    diabetes 2

  14. jar says:

    Thanks for your code.

    I modified your approach:


    [[NSInvocation invocationWithTarget:someLabel
    selector:@selector(setFrame:)
    retainArguments:NO, CGRectMake(40, 40, 200, 100)]
    invokeOnMainThreadWaitUntilDone:NO];

    to something, that can be use like this:


    NSInvocation * inv = [NSInvocation invocationWithTarget:someLabel
    selector:@selector(setFrame:)];
    [inv setMethodParameter:&CGRectMake(40, 40, 200, 100) atIndex:0];
    [inv invokeOnMainThreadWaitUntilDone:NO];

    invocationWithTarget:selector: is just the wrapper of default initializer (creates signature with use of target and selector, calls invocationWithMethodSignature.., sets target and selector to created NSInvocation object)

    setMethodParameter: wrapps original NSInvocation setArgument (with aligned index)

    Now UIInvocation category has just few lines of code and 0 magic:). I think, this simplified solution is not bad too!

  15. [...] previously wrote a blog post titled Performing any Selector on the Main Thread detailing a convenience category on NSInvoication for easily creating invocation objects that could [...]

  16. [...] as easily been done using GCD. The additions are described in detail and available for download in this blog post, or from in the CWFoundation repo on [...]

  17. Xero says:

    I seem to have found a bug in

    NSGetSizeAndAlignment(type, &size, &align);

    which outputs an alignment always equal to size. This doesn’t work with any argument that’s < 4 bytes because the alignment is not correct. on iOS a byte or short is aligned at the 32 bit word boundary when passed to a function. However NSGetSizeAndAlignment gives an alignment of 1 or 2.
    I seem to have fixed the problem by adding
    align = min(align, size(int))
    However I'm making an assumption on how the arguments are passed which is likely to change with different architectures, its not very portable. For instance after GNUC 2 va_list is not a void* but a struct.

    Do you know of a portable way to decode the va_list?

    • Yes it is a bug in the original implementation. The ZIP-file with code that is supplied in the Update 2 footer has this bug fixed.

      The alignment can change with different architectures, and ABI:s. The ABI for Mac OS X and iOS is quite stable though, so I do not see any cause to worry.

  18. John Haney says:

    How about using GCD and blocks for moving things to the main thread instead?

    dispatch_async(dispatch_get_main_queue(), ^{ [UIView setHidden:YES]; });

    Or, to wait for the execution to complete…

    dispatch_sync(dispatch_get_main_queue(), ^{ [UIView setHidden:YES]; });

    • GCD works well for most cases but have two gotchas:
      1. It does not work pre-iOS 4.0, can be a showstopper for many projects.
      2. Blocks do not handle memory, that is any block captured variable for an object instance is not retained by the block. So the simple example:
      dispatch_async(dispatch_get_main_queue(), ^{ myView.hidden = YES; });
      I no quite that simple since there is no guarantee that myView will actually still be alive when the code is executed some time later. The safe code would be:
      [myView retain]; dispatch_async(dispatch_get_main_queue(), ^{ myView.hidden = YES; [myView release]; });
      Add more captured variables and more complex use cases and it can get hairy fast. My NInvocation category abstracts this away in the single argument preceding the argument list retainArguments:YES, ....

      Both works, and I use both myself. I just find that my invocation solution handles more of the details for me in most use-cases.

  19. Arun GJ says:

    Thank you. It helped me a lot.

  20. [...] about why there’s tokens in the template and pointing out that you’ll also need the variable argument invocation support that we mentioned a while [...]

  21. [...] to use the templates, you’ll need a couple of categories on NSInvocation that you can find here. I forgot that those methods weren’t part of NSInvocation because I use them so [...]

  22. Dave Murdock says:

    Thanks for the code.

    The for loop index should be an NSUInteger not just an int. I turned on the GCC 4.2 Sign Comparison warning and it flagged this because [signature numberOfArguments] returns an NSUInteger. Also, the variable name “index” causes the Hidden Local Variables GCC 4.2 warning to be thrown, so I renamed it to arrayIndex.

    One more thing, the code in invokeInBackground seems like it’s from your app, should the only code in that method be this?
    [self performSelectorInBackground:@selector(invoke) withObject:nil];

  23. iWyre says:

    [...] to use the templates, you’ll need a couple of categories on NSInvocation that you can find here. I forgot that those methods weren’t part of NSInvocation because I use them so [...]

  24. [...] a neat trick: parsing a variable argument list into an NSInvocation call in order to call an arbitrarily [...]

  25. [...] Performing any Selector on the Main Thread | Jayway Team Blog – Sharing Experience (tags: objective-c threads) [...]

  26. todd says:

    Christopher – NSDictionary isn’t ordered and presumably you need to also encode the order of the parameters.

  27. Is there some compelling reason to jump through these hoops, rather than simply sending through an NSDictionary with all the values you could possibly hope to send? Yes, you can only send one argument, but how much more could you need to send than, “everything you can pack into a dictionary” ? Or better yet, define your own class and send an object of that type through. Then you can pack in primitive values if you so choose. I feel this is a clever engineering solution to a problem that is better solved through design.

    • @Christopher Drum: Yes, mainly because this is a category you write once and drop into your projects, in order to not jump through this hoop ever again.

      If you send the arguments in an dictionary then you have to: 1. Pack the arguments, 2. Send packed argument to placeholder method, 3. Unpack arguments, 4. Call actual method. Using this method you instead: 1. Call actual method. Each extra step you have to take, each line of code, is a potential hiding place for bugs.

      So convenience, maintainability, and safety, are the compelling reasons.

  28. todd says:

    This is interesting. Thanks. Too bad there isn’t a cleaner way of creating the NSInvocation… the byte buffer va_arg() stuff seems a bit close to the metal as it were. What happens on a PowerPC? byte order issues taken care of? etc.

    • @todd: Not as high level as most code, but everything is according to spec; C99, Apple’s public Cocoa API, and Darwin ABI. So very safe as long as you target existing Darwin platforms.

  29. Kalle says:

    I can see that this might be useful in edge cases, but I would also take it as a design warning if my background tasks ties in with my view without going through the view controller (which probably shouldn’t be threaded by itself). Most tasks I perform in background threads is stuff that really shouldn’t depend on my view. In which cases do you use this?

    • The trigger that got me started was a call to -[UIProgressView setProgress:], that takes a float, and thus needed object wrapping of the argument.

      I see and understand your point, the model should not update the view directly. As with any tool, it can be misused. But however you design your app, sooner or later the view needs to know about what the background tasks has done. And when that time comes you need to cross thread boundaries. I rather do it using an easy convenience method, than manually bundling data, manual work is alway error-prone.

Leave a Reply