Skip to content

21 — Common GCD Gotchas

rebeloper edited this page Jul 12, 2026 · 1 revision

🍽️ Intuition

A rundown of classic kitchen mistakes: a waiter who forgets to tell the head chef the dish is ready (forgetting to hop back to the main queue), a chef who keeps holding onto a ticket for a customer who already left (retain cycles), and a rush-order chef getting stuck behind a slow prep chef who's holding the one shared knife (priority inversion).

🧠 What It Means In Swift

Three concrete gotchas:

  1. Retain cycles — capturing self strongly in a closure that outlives it, fixed with [weak self].
  2. Forgetting to hop back to main — background work that mutates UI without dispatching back to DispatchQueue.main, causing updates off the main thread.
  3. Priority inversion — low-QoS work holding a shared lock blocks high-QoS work waiting on that same lock, effectively dragging the high-QoS work down to low priority.

📊 ASCII Diagram

1) Retain cycle:     self ──strong──▶ closure ──strong──▶ self   (never released)
2) Forgotten hop:    background work ──▶ label.text = x          (off main thread!)
3) Priority inversion: [high-QoS] waits ──▶ [low-QoS holds lock] ──▶ (blocked)

💻 Tiny Swift Example

// Gotcha 1: retain cycle
queue.async { [weak self] in
    self?.doWork()
}

// Gotcha 2: forgetting to hop back to main
queue.async {
    let result = compute()
    DispatchQueue.main.async {
        self.label.text = result // UI updates must happen here
    }
}

🔄 vs. Swift Concurrency

@MainActor eliminates gotcha 2 at compile time (you can't accidentally touch UI off the main actor), and structured concurrency's automatic cleanup reduces (but doesn't eliminate) gotcha 1's surface area — priority inversion (gotcha 3) can still happen with actors, though the scheduler manages it differently.

🧸 Memory Sentence

Most GCD bugs are one forgotten hop, one forgotten [weak self], or one shared bottleneck.

⚠️ Analogy Limit

Real kitchen mistakes get noticed immediately by a human; these three gotchas often produce silent, intermittent bugs that only show up under specific timing conditions.

✅ Check Your Understanding

A closure captures self strongly, is stored as a long-lived property on self, and is never called again after being set — which of the three gotchas above is this, and why does it leak?

Clone this wiki locally