5

So i have this code:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

//Bunch of code

NSLog(@"Test");

});

which runs and returns nslog immediately. But the results of the code only appear on the screen a few seconds delay. Is there a problem with nslog being used here, which means it's called before it would usually, making it seem speedy when really it isn't. I'm confused as to where this delay is coming from, as the NSLog is right at the end of all the code which runs then.

Also, another solution to my problem, is it possible to get an NSLog when every method is called (a bit like NSZombiesEnabled i suppose) so i can make sure there isn't some bit which i didn't notice taking up the sweet response time of my app?

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
Andrew
  • 15,935
  • 28
  • 121
  • 203
  • 3
    Why would you expect your log to show up immediately? Your "bunch of code" might take a few seconds to run, delaying the log by that much, because it will only execute after the rest of your block's code has. – Brad Larson Aug 08 '11 at 18:22
  • This is what confuses me. It's after every bit of code, and yet shows up immediately while the code takes a few seconds to show it's results on screen. – Andrew Aug 08 '11 at 18:47
  • 1
    OK, so you're saying that the NSLog statement has its text show up immediately on the console, but your "bunch of code" contains within it other UI updates that do not take effect onscreen immediately? If that's the case, what kind of UI updates are they? Are they dispatched asynchronously on the main thread? Is there a process that might be caught in a tight loop on the main thread after this block is dispatched, potentially preventing UI updates? – Brad Larson Aug 08 '11 at 18:52
  • What Brad said; without knowing what `bunch of code` is doing and, in particular, exactly what is taking a long time to show up (and how that update is triggered), there is no way to say. – bbum Aug 08 '11 at 19:50

2 Answers2

12

UIKit isn’t thread-safe. Any calls you make that affect your UI need to be made on the main thread, or you’ll get weird, unpredictable behavior. Example:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // Background code that doesn't need to update the UI here

    dispatch_async(dispatch_get_main_queue(), ^{
        // UI code here
    });
});
Noah Witherspoon
  • 57,021
  • 16
  • 130
  • 131
  • This is a great answer because I think that it's how most event-response work needs to happen: flip from the Main queue to a Worker queue (essentially) and then return to the Main queue for any UIKit code (as little as possible). This code is also relevant, I think: http://stackoverflow.com/a/8186206/8047 – Dan Rosenstark Nov 23 '12 at 14:01
  • Sorry, just read the answer I linked to again: if you're dispatching async to the main thread, you don't have to worry if you're on the main thread in the first place. – Dan Rosenstark Nov 23 '12 at 14:07
11

Recently I had a similar problem myself. Most likely your delayed updates are stuff you are doing on the user interface. And here's the problem: you are not running in the main thread of the application (also called the UI thread). Therefore, whatever you change to the user interface won't actually be visible until... it feels like showing up, which tends to happen with a redraw triggered by something else in the application.

What you have to do is inside your block queue your graphical updates in the UI thread like this:

dispatch_async(dispatch_get_main_queue(), ^{
        // Do GUI stuff.
    });

Sometimes your block might be big or just using code which was previously run on the main thread, so it is difficult to move all of it to a specific place to run it on the main queue. For these situations, queue a forced refresh in the UI thread at the end of your block:

dispatch_async(dispatch_get_main_queue(), ^{
        [self.somewidget setNeedsDisplay];
        [self.view setNeedsDisplay];
        [self.dontforgetme setNeedsDisplay];
    });

With regards to your "app taking up the sweet response time of my app", it seems that you are trying to use GDC to avoid blocking the user interface. To make sure programatically that you don't happen to do CPU intensive operations in the UI thread, and vice versa, you run GUI updates in the UI block, I've made myself the following macros:

/// Stick this in code you want to assert if run on the main UI thread.
#define DONT_BLOCK_UI() \
    NSAssert(![NSThread isMainThread], @"Don't block the UI thread please!")

/// Stick this in code you want to assert if run on a background thread.
#define BLOCK_UI() \
    NSAssert([NSThread isMainThread], @"You aren't running in the UI thread!")

As you can see by the comments, I tend to use these macros at the beginning of methods I want to make sure I'm not using by error in the wrong place. I've put these macros and more random stuff at https://github.com/gradha/ELHASO-iOS-snippets which you may find useful.

Grzegorz Adam Hankiewicz
  • 7,349
  • 1
  • 36
  • 78