How does ARC interact with Swift concurrency?
The baseline: ARC doesn’t know about concurrency, and never needed to
ARC’s job is unchanged: retain on copy/capture, release on scope exit, deinit at zero.
1. Retain/release counts must themselves be thread-safe
Swift’s ARC implementation uses atomic operations for retain/release — this was true even in the GCD era, so concurrency didn’t introduce this cost.
2. Sendable is what prevents ARC-safe-but-logically-unsafe sharing
ARC keeps an object from being deallocated prematurely even if shared across threads — but says nothing about the object’s contents. Sendable closes that gap.
3. Actors change where ARC traffic can happen, not whether
Cross-actor calls still retain/release arguments and results as they cross.
4. Suspension points and retain/release lifetime
A value held via a strong reference across an await keeps its reference count elevated — ARC guarantees memory safety across the suspension, but says nothing about logical staleness (reentrancy).
5. Task closures and captured references
class Model { var data: [Int] = [] }
func launch(_ model: Model) {
Task {
model.data.append(1) // ARC: fine. Sendable checker: ❌ unless Model is Sendable
}
}
ARC is the mechanism that keeps memory valid; Sendable/isolation is the separate layer deciding whether sharing that memory is logically safe.
6. Weak/unowned references and actor-isolated closures
[weak self] in Task { } bodies applies identically to any long-lived closure context.
7. Global actors and deinit
deinit is inherently non-isolated and cannot await or safely touch other actor-isolated state.
Summary
| Layer | Guarantees | Does NOT guarantee |
|---|---|---|
| ARC (atomic retain/release) | Object won’t be freed while referenced, even across threads | Contents are safe from concurrent mutation |
Sendable/isolation checking | Values crossing domains have no unsynchronized shared mutable state | Logical correctness |
| Actor isolation | One task touches actor-isolated state at a time | State unchanged across await (reentrancy) |
One-liner if pressed: “ARC has always used atomic retain/release since objects could be shared across threads even before structured concurrency — so ARC alone keeps memory valid, but says nothing about whether an object’s contents are safe to touch concurrently; that’s the gap Sendable and actor isolation close on top of ARC.”