dispatch_async versus Task.detached for offloading work
GCD versus structured concurrency.
dispatch_async submits a closure to a queue; Task.detached starts an unstructured async task off the current actor without inheriting context.
What's really being asked
It checks whether you understand both concurrency models and resist overusing Task.detached, which discards the context that structured concurrency exists to preserve.
Gcd
DISPATCH_ASYNC
dispatch_async submits a closure to a serial or concurrent dispatch queue backed by a thread pool. There is no await, no cancellation propagation, and no structured parent-child relationship; you manage callbacks and synchronization yourself. It remains useful for low-level scheduling, integrating with callback-based APIs, and fine-grained queue control.
Swift concurrency
TASK.DETACHED Task.detached spawns an unstructured task that explicitly does not inherit the current actor, priority, or task-local values, so its body runs off the main actor by default. Because it is detached it is not part of the surrounding task tree, so cancellation and structured cleanup do not flow automatically. A plain Task, by contrast, inherits context and is usually what you want.
Which to choose
For offloading CPU work in an async codebase, prefer calling an async function or putting the work behind an actor or a nonisolated function, letting the runtime hop executors. Use Task.detached only when you genuinely must break actor inheritance, for example to avoid hopping back to the main actor for purely background work. Use dispatch_async when interoperating with existing GCD code or needing explicit queue semantics.
The mistakes people make
Treating Task.detached as the standard way to leave the main thread, which loses priority and task-local propagation and invites data races. Believing dispatch_async participates in Swift cancellation.
What usually comes next
What does a non-detached Task inherit? How does actor isolation move work off the main thread without detaching? How does cancellation differ between the two? What are task-local values?
A concrete example
Inside a @MainActor view model you need to parse a large file. Writing Task.detached { parse() } leaves the main actor but drops priority and cancellation. A cleaner approach is an actor or nonisolated async function you await, which offloads the work while keeping the task tree, cancellation, and priority intact.
Interview question
Why is reaching for Task.detached as the default way to move work off the main thread often a poor choice?
- a.It is slower than dispatch_async in every benchmark
- b.It cannot run code off the main thread at all
- c.It drops inherited priority, task-local values, and structured cancellation from the surrounding taskCorrect
- d.It permanently disables the main actor for the app
Why? this is the answer
A detached task deliberately escapes actor and task context, losing priority, task-locals, and structured cancellation. Plain Task or actor isolation usually offloads work while keeping that context.
Just read this? Test yourself on what you have been reading.
Read the original → docs.swift.org
- #swift
- #concurrency
- #gcd
- #async-await
- #actors
Put your scrolling time to good use
Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on swift — each one lists the topics its interview covers.
See open roles