19

I believe I understand what the dispatch queue is doing when I call it, but I'm not sure when exactly I should use it and what it's advantages are when I do use it.

If my understanding is correct, DispatchQueue.main.async { // code } will schedule the code contained within the closure to run on the main dispatch queue in an asynchronous manner. The main queue has the highest priority, and is typically reserved for updating UI to maximize App responsiveness.

Where I'm confused is: What exactly is the difference in updating UI elements within a dispatch queue closure versus just writing the code outside the closure in the same spot? Is it faster to execute the code in the body of a view did load method rather than sending it to the dispatch queue? If not, why?

Code Example:

class MyViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
    }

}

Versus:

class MyViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        DispatchQueue.main.async {
            updateUI()
        }
    }
}

Which one is will update the UI faster?

pkamb
  • 33,281
  • 23
  • 160
  • 191
ICW
  • 4,875
  • 5
  • 27
  • 33

2 Answers2

28

The primary use of DispatchQueue.main.async is when you have code running on a background queue and you need a specific block of code to be executed on the main queue.

In your code, viewDidLoad is already running on the main queue so there is little reason to use DispatchQueue.main.async.

But isn't necessarily wrong to use it. But it does change the order of execution.

Example without:

class MyViewController: UIViewController {
    func updateUI() {
        print("update")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        print("before")
        updateUI()
        print("after")
    }
}

As one might expect, the output will be:

before
update
after

Now add DispatchQueue.main.async:

class MyViewController: UIViewController {
    func updateUI() {
        print("update")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        print("before")
        DispatchQueue.main.async {
            updateUI()
        }
        print("after")
    }
}

And the output changes:

before
after
update

This is because the async closure is queued up to run after the current runloop completes.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • 2
    How do we know which functions are or aren't run in the main queue? – pete Aug 04 '20 at 06:22
  • 1
    `print("Is main queue: \(Thread.isMainThread)")` – José Mar 29 '22 at 11:47
  • An example where this is needed is when you're trying to seek an AVPlayer in the viewDidLoad. It needs to complete the function before it can seek so calling it in DispatchQueue.main.async will allow it to work. – 6rchid Jul 16 '23 at 00:15
1

I just ran into the exact situation discribed in your Question: viewDidLoad() calling DispatchQueue.main.async.

In my case I was wanting to modify Storyboard defaults prior to displaying a view.

But when I ran the app, the default Storyboard items were momentarily displayed. The animated segue would finish. And only THEN would the UI components be modified via the code in viewDidLoad(). So there was this annoying flash of all of the default storyboard values before the real values were edited in.

This was because I was modifying those controls via a helper function that always first dispatched to the main thread. That dispatch was too late to modify the controls prior to their first display.

So: modify Storyboard UI in viewDidLoad() without dispatching to the Main Thread. If you're already on the main thread, do the work there. Otherwise your eventual async dispatch may be too late.

pkamb
  • 33,281
  • 23
  • 160
  • 191
  • related: https://stackoverflow.com/questions/10330679/how-to-dispatch-on-main-queue-synchronously-without-a-deadlock – pkamb Nov 05 '19 at 01:38