views:

2445

answers:

4

If I spawn a new thread, and then within it I push a new controller onto my UINavigationController, using code like this...

(a) not working

-(void)myCallbackInThread
{
    // move on...
    UIApplication* app = [UIApplication sharedApplication];
    [app changeView];
}

then I find that the view appears, but does not respond to user input.

If I change the code like this

(b) working

-(void)myCallbackInThread
{
    // move on...
    UIApplication* app = [UIApplication sharedApplication];
    [app performSelectorOnMainThread:@selector(moveToMain) withObject:nil waitUntilDone:FALSE];
}

Then everything works just fine.

Any hints as to why?

+2  A: 

Just found this in the iPhone threading docs

If your application has a graphical user interface, it is recommended that you receive user-related events and initiate interface updates from your application’s main thread. This approach helps avoid synchronization issues associated with handling user events and drawing window content. Some frameworks, such as Cocoa, generally require this behavior, but it also has the advantage of simplifying the logic for managing your user interface.

I still don't see what would actually cause something to display but not be able to receive user input, but I'll follow that guideline in future.

Airsource Ltd
+2  A: 

As the documentation says, "If you’re not sure about a particular graphical operation, plan on doing it from your main thread."

A good rule of thumb to follow is that, if a class isn't explicitly documented as being thread-safe, then it's probably not. Additionally, code that's not documented as being thread-safe may not fail fast when used by multiple threads, but may simply exhibit undefined behavior, as you saw.

Evan DiBiase
+1  A: 

In your case, it really depends on what's happening in [app changeView], but the reason it stops responding is most likely that you have no run loop dispatching events on your new, secondary thread (more on this below). In general, however, it is a very bad idea to update the GUI from a secondary thread. As you've already discovered, all of these events should go through the main thread.

The main reason that your second example works and not your first is that UIApplication sets up and handles the run loop and event dispatcher for you on the main thread. So, when you call performSelectorInMainThread, the selector gets dispatched to the main run loop which is able to then handle your gui input and other events. The event dispatcher is also run and managed by UIApplication on the main thread.

So basically, don't perform any GUI management activities on a secondary thread. Dispatch those to the main thread. And if you need processing on a secondary thread (for things like timers or asynch calls, etc.) then you have to start and manage your own run loop on that thread (see NSRunLoop for more on managing your on run loop).

Jason Coco
A: 

Almost none of the UI code in UIKit or AppKit is threadsafe. How it fails is irrelevent, because if you are worrying about how it fails you are doing something that is going to result in all sorts of weird bugs that will subtly change between different OS release anyway.

My best advice is to not use things from background threads unless the docs say it is safe.

Louis Gerbarg