Deinitialization
A deinitializer is called immediately before a class instance is freed.
It is the mirror image of initialization: where init sets an
object up, deinit gives it one last chance to clean up after
itself. This is a short chapter because Swift handles most cleanup for
you — but knowing exactly when deinit runs is essential for
managing external resources correctly.
How deinitialization works
You write a deinitializer with the deinit keyword. Like an
initializer it has no func keyword, and unlike an
initializer it takes no parameters and no parentheses. Each class may
have at most one deinitializer.
class FileHandle {
let path: String
init(path: String) {
self.path = path
print("Opened \(path)")
}
deinit {
print("Closed \(path)") // cleanup runs here
}
}
A deinitializer can access any property of the instance and is typically used to release resources the object owns — close a file, sever a network connection, unsubscribe from a notification, or free a manually allocated buffer.
When the deinitializer is called
Swift calls deinit automatically the moment an instance is
about to be deallocated — you never call it yourself. Deallocation
happens when the last strong reference to the instance goes away, which
Swift tracks with Automatic Reference Counting (ARC).
Common moments include a variable going out of scope, being reassigned
to something else, or being set to nil.
Superclass deinitializers run automatically: a subclass's
deinit is called first, then its superclass's, all the way
up the chain. You don't (and can't) call super.deinit()
manually.
Watch out: Don't rely on deinit for
timing-critical or guaranteed-prompt cleanup. It fires only when the
instance is actually deallocated — and if a strong reference cycle keeps
it alive, it may never fire at all. See
Automatic Reference Counting
for how to break those cycles with weak and
unowned.
Deinitializers in action
Here's a small game where a Bank tracks a finite supply of
coins and a Player returns its coins to the bank when it
leaves the game. Setting the player to nil removes the only
strong reference, so the deinitializer runs.
class Bank {
static var coinsInBank = 10_000
static func distribute(coins requested: Int) -> Int {
let given = min(requested, coinsInBank)
coinsInBank -= given
return given
}
static func receive(coins: Int) {
coinsInBank += coins
}
}
class Player {
var coinsInPurse: Int
init(coins: Int) {
coinsInPurse = Bank.distribute(coins: coins)
}
func win(coins: Int) {
coinsInPurse += Bank.distribute(coins: coins)
}
deinit {
Bank.receive(coins: coinsInPurse) // hand coins back
}
}
var player: Player? = Player(coins: 100)
print(Bank.coinsInBank) // → 9900
player!.win(coins: 2_000)
print(player!.coinsInPurse) // → 2100
print(Bank.coinsInBank) // → 7900
player = nil // last reference gone → deinit runs
print(Bank.coinsInBank) // → 10000 (coins returned)
Assigning nil deallocates the player, triggering deinit and returning its coins.
The relationship to ARC
Swift manages class instance memory through ARC. Every instance keeps a
count of the strong references to it; when that count reaches zero, ARC
deallocates the instance and calls its deinit first. You
never free memory by hand — your job is simply to make sure references
are released at the right time, which usually means avoiding strong
reference cycles. Because ARC's bookkeeping is deterministic (not a
garbage collector that runs later), deallocation — and thus
deinit — happens precisely when the last reference drops.
Tip: A handy way to confirm an object is being freed
as expected is to drop a print in its deinit
during development. If the message never appears when you expect it to,
you likely have a retain cycle leaking the instance.
Only classes have deinitializers
Deinitializers exist only on classes. Structures and
enumerations are value types: they are copied rather than referenced,
their lifetime isn't governed by ARC, and they have no concept of being
the "last reference." Consequently they cannot define deinit.
If a value type needs to perform cleanup tied to a resource, it typically
wraps a class instance that owns the resource and lets that class's
deinitializer do the work.
Wrapping up
A deinitializer is your one guaranteed hook just before a class instance
disappears: write deinit, release whatever the object owns,
and let ARC decide the timing by reference counting. Keep cleanup
idempotent and avoid relying on it for prompt resource release. Next,
we'll see how optional chaining lets you safely reach
into objects that might be nil.