How does ARC work, and how is it different from garbage collection?
Predict first
In what order does this print?
final class Res {
init() { print("init") }
deinit { print("deinit") }
}
func work() {
let r = Res()
print("working")
}
work()
print("done")
- init, working, done, deinit
- init, working, deinit, done
- init, deinit, working, done
Predict first
What does this print?
final class Res {
init() { print("init") }
deinit { print("deinit") }
}
var r: Res? = Res()
r = nil
print("after")
- init, after, deinit
- init, deinit, after
- init, after
Mental model. ARC is a library book with a sign-out list. The moment the last borrower returns it, the book is shredded on the spot, with no janitor involved.
ARC (automatic reference counting) is done by the compiler. It inserts retain and release calls around your code so that every class instance carries a count of its strong references. When the count drops to zero the object’s deinit runs immediately and its memory is freed. There is no background collector and no pause: death is deterministic.
A tracing garbage collector works the other way round: it periodically finds everything unreachable from the roots, including cycles, but you cannot say when. ARC’s price is paid on every copy of a reference (an atomic increment or decrement) and it cannot reclaim cycles, which is why weak and unowned references exist. ARC applies to class instances, closure contexts and boxes; plain structs and enums with no references need none of it.
The only strong reference is the local r. It goes away when work() finishes, so deinit runs before the caller continues.
Assigning nil drops the last strong reference, so the object dies on that line, before after.
Common traps
- “ARC is a kind of garbage collection.” There is no collector, no heap scan, and no way to reclaim a cycle.
- “
deinitruns at the closing brace.” It runs when the last reference is released, which the compiler places for you; see the lifetime question.
They might follow up with
- Is ARC thread-safe? The count updates are atomic, so retaining from many threads is safe. Mutating the object’s own properties is not.
- What does ARC cost? Atomic operations on every reference copy. The optimiser removes many pairs, but hot loops over class references still show it.
Related questions
- 3.1.2 What is a reference count, and how do you check that you hold the only reference?
- 3.1.3 When exactly is an object deallocated: at the end of its scope, or after its last use?
- 3.3.1 What is a retain cycle, and how do you find and fix one?
What is a reference count, and how do you check that you hold the… →