4

In my application, I let a progress indicator starts animation before I send a HTTP request. The completion handler is defined in a block. After I get the response data, I hide the progress indicator from inside the block. My question is, as I know, UI updates must be performed in the main thread. How can I make sure it?

If I define a method in the window controller which updates UI, and let the block calls the method instead of updating UI directly, is it a solution?

Stephen Hsu
  • 5,127
  • 7
  • 31
  • 39

2 Answers2

10

Also, if your app targets iOS >= 4 you can use Grand Central Dispatch:

dispatch_async(dispatch_get_main_queue(), ^{
    // This block will be executed asynchronously on the main thread.
});

This is useful when your custom logic cannot easily be expressed with the single selector and object arguments that the performSelect… methods take.

To execute a block synchronously, use dispatch_sync() – but make sure you’re not currently executing on the main queue or GCD will deadlock.

__block NSInteger alertResult; // The __block modifier makes alertResult writable
                               // from a referencing block.
void (^ getResponse)() = ^{
    NSAlert *alert = …;
    alertResult = [NSAlert runModal];
};

if ([NSThread isMainThread]) {
    // We're currently executing on the main thread.
    // We can execute the block directly.
    getResponse();
} else {
    dispatch_sync(dispatch_get_main_queue(), getResponse);
}

// Check the user response.
if (alertResult == …) {
    …
}
gcbrueckmann
  • 2,413
  • 18
  • 10
  • May I embed this dispatch_async(dispatch_get_main_queue(), ^{}); inside another block? In my case, I need the block to be executed synchronously on the main thread. I'll show a NSAlert and ask users to respond to it. – Stephen Hsu Aug 27 '11 at 06:44
  • 1
    Sure, you can nest blocks any way you want. The only caveat is that you cannot submit a block for _synchronous_ execution to the current queue, i. e. `dispatch_sync()` to the queue your code is currently executing on (GCD would deadlock). But this doesn't apply to the _asynchronous_ method using `dispatch_async()` as in the answer example above. If you need to `dispatch_sync(dispatch_get_main_queue(), …)` make sure `dispatch_get_current_queue()` isn't the main queue. `[NSThread isMainThread]` will serve the same purpose in your example. – gcbrueckmann Aug 27 '11 at 07:14
  • Thanks. Now my understandings of block and main thread are more clear. – Stephen Hsu Aug 27 '11 at 10:02
0

You probably misunderstood something. Using blocks doesn't mean that your code is running in a background thread. There are many plugins that work asynchronously (in another thread) and use blocks.

There are a few options to solve your problem.

You can check if your code is running in the main thread my using [NSThread isMainThread]. That helps you to make sure that you're not in the background.

You can also perform actions in the main or background by using performSelectorInMainThread:SEL or performSelectorInBackground:SEL.

The app immediately crashes when you're trying to call the UI from a bakcground thread so it's quite easy to find a bug.

lbrndnr
  • 3,361
  • 2
  • 24
  • 34
  • Thanks. I run [NSThread isMainThread] inside the block, and it returns NO. It's a Mac app, and it doesn't crash. In the method - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait, the type of arg is id. How to pass BOOL to utilize this method? – Stephen Hsu Aug 27 '11 at 06:37
  • The app doesn't crash when you update the UI inside the block? Try [NSNumber numberWithBOOL:YES] – lbrndnr Aug 27 '11 at 07:07
  • I run it again, [NSThread isMainThread] returns 0, it's NO, right? In the block, I hide a progress indicator, I think it a UI update. And the app doesn't crash. – Stephen Hsu Aug 27 '11 at 07:31
  • Yes it's actually no. Anyway try if([NSThread isMainThread]) { NSLog(@"ok"); }. Is there a ok in the log? – lbrndnr Aug 27 '11 at 08:35
  • My code is if ([NSThread isMainThread]) {NSLog(@"Main Thread");} else {NSLog(@"NO");}. It prints 'NO'. – Stephen Hsu Aug 27 '11 at 08:46