Posts in gcd

  • Verifying UIKit is Running on the Main Thread

    As previously mentioned: to get the UI to update, you need to run your
    UIKit configuring code on the main thread. Peter Stienberger has posted a
    great snippet of code that raises an assertion whenever you attempt to
    access UIKit code from off of the main thread. Simply add the file to your
    project (and swap out the PSPDFLogError with an NSLog) and build and run.
    Everytime you call UIKit code off of the main thread, it'll crash your app.
    Great for finding bugs.

    ❖
  • Forcing Things to Run on the Main Thread

    The Problem

    So Apple recommends that we avoid doing too much work on
    the main thread. This is great advice, the less time you spend wasting CPU
    cycles in the main thread, the more responsive your UI will be. In my opinion a
    well defined iOS application will avoid doing more than update the UI on the
    main thread.

    One prime example I've run into was some code that did a synchronous network
    request. It waited for the network response before returning and someone
    thought it had to run on the main thread. This resulted in a lock-up during
    some animations I was working on.

    There's a number of topics[1] that I plan to discuss about getting things
    off of the main thread, but today I'm going to discuss running things on the
    main thread.

    If you've set up your app to do all of the heavy lifting in some secondary
    thread or in a dispatch queue, you will probably need to update you
    UI based on this work. The first thing you're likely to do is simply update the
    UIView object:

    {% highlight objc %}
    someUILabel.text = updatedString;
    {% endhighlight %}

    Well, it turns out that doesn't really do anything. You might get really lucky
    and see the label update seconds or minutes later. The first thing to do is by
    adding a call to setNeedsDisplay.

    {% highlight objc %}
    someUILabel.text = updatedString;
    [someUILabel setNeedsDisplay];
    {% endhighlight %}

    The next time the UI code checks to see if the UILabel has changed, it will see
    that, yes it has, and upate the display. Sadly, when this happens is often
    still fairly late.

    The Solution

    To really get things updated, setNeedsDisplay needs to be called from the
    main thread, where the UI does its work. If you can ensure that your code runs
    there, it should update with no noticable delay. Thankfully, Apple gave us a
    wonderful tool for executing small bits of code anywhere on a specific thread
    or queue.

    {% highlight objc %}
    dispatch_async(dispatch_get_main_queue(), ^{
    someUILabel.text = updatedString;
    [someUILabel setNeedsDisplay];
    });
    {% endhighlight %}

    Suddenly your label updates the instant your code runs. The best part is it can
    be put in anywhere and since it runs on the main thread, the UI will update
    almost immediately.

    So when you need to get some code to run on the main thread, usually to get
    your UI to update with new information, you just use dispatch_async and
    dispatch_get_main_queue(). Now go forth, and write responsive UIs!


    1. These include handling network calls, generally working with Grand Central Dispatch, and some common pitfalls with blocks. Look for them soon. ↩ī¸Ž

    ❖