Reference counting
What you'll learn:
- explain when Swift frees a class object and when
deinitruns; - spot a strong reference cycle that keeps objects from being freed;
- break such a cycle with
weakandunownedand choose between them; - write
[weak self]in a closure that the object itself stores.

Picture a book on a lectern in a library reading room. Everyone who works with it gets a borrower's card. A dial on the lectern shows how many cards are out: a card taken is plus one, a card returned is minus one.
While at least one card is out, someone needs the book, and it stays on the lectern. When the last card comes back, the dial shows zero, and the librarian puts the book back in its spot on the shelf right away.
This is how Swift treats class objects. Each object takes up room in memory. While the object is needed, it stays there, and afterwards the memory has to be given back, or the program only keeps growing. The question is how to tell that an object is no longer needed. Let's see when that happens:
class Book {
let title: String
init(title: String) {
self.title = title
}
deinit {
print("\(title) is back")
}
}
var anna: Book? = Book(title: "Dune")
print("Anna is reading") // Anna is reading
anna = nil // Dune is back
print("The end") // The endLet's go through the program line by line:
- The class
Bookstores a title, and thedeinitblock prints a line when the book is freed. var anna: Book? = Book(title: "Dune")creates an object and puts a reference to it intoanna— a borrower's card. The type is optional so thatnilcan go there later.print("Anna is reading")prints a line: the book is withanna.anna = nilreturns the card. There are no other references to the book, anddeinitruns right on this line: the program printsDune is back.print("The end")prints the last line, when the book is already gone.
The deinit block
Swift calls the deinit block right before it frees the object. It is the counterpart of init: init gets the object ready for work, and deinit runs at the very end of its life.
What matters about deinit:
- it is written without parentheses or parameters:
deinit { … }; - Swift calls it itself, and the line
book.deinit()does not compile; - the object's properties are still available inside, so
deinitcan printtitle; - classes have
deinit, and the structs you have written do not.
In real programs, deinit closes what the object opened, such as a file. In the lessons of this theme, deinit prints a line, so you can see when an object is freed.
How Swift counts references
Swift keeps a count for every class object, like the librarian at the lectern. This way of managing memory is called ARC — automatic reference counting.
A reference that holds an object is called a strong reference. Every reference you have written so far is strong. An object is held by:
- a variable or a constant;
- a property of another object;
- an element of an array.
The count changes by three rules:
- A new strong reference adds one.
- A reference that goes away takes one away.
- When the count reaches zero, Swift calls
deinitand frees the object's memory right away.
Click "Next step" and watch the count in the main frame: deinit runs on the line where the count reaches zero.
1class Book {2 let title: String3 init(title: String) {4 self.title = title5 }6 deinit {7 print("\(title) is back")8 }9}10 11var anna: Book? = Book(title: "Dune")12var boris = anna13anna = nil14print("Anna is done")15boris = nil16print("Boris is done")
Book(title:) creates an object, and init fills in its title property.
The line anna = nil did not free the book: boris still had its own card. An object lives while at least one strong reference to it exists, no matter which of them came first.
When a reference goes away
The variable is set to nil
That is what happened in the examples above. To put nil into a variable, the variable needs an optional type, such as Book?.
Another object goes into the variable
The old reference goes away:
var boris: Book? = Book(title: "Emma")
boris = Book(title: "Ulysses") // Emma is back
print(boris?.title ?? "none") // Ulyssesboris lets go of the book Emma and takes Ulysses. There are no other references to Emma, so its deinit runs on the assignment line.
The function ends
The reference in a constant inside a function goes away by the end of the call at the latest:
func visit() {
let book = Book(title: "Odyssey")
print("Reading \(book.title)") // Reading Odyssey
}
visit() // Odyssey is back
print("After visit") // After visitThe book Odyssey is freed before the program prints After visit. Swift does not promise the exact line: the reference in the constant may go away before the function ends, right after the last use of book. That is why the lessons set the moment of freeing explicitly, with nil.
The program ends
If a reference to an object still exists when the program ends, its deinit does not run at all. The program finishes as a whole, and the system takes back all its memory at once. So do not wait for a line from the deinit of an object that stays in a variable to the end.
ARC and a garbage collector
Here the library picture is off. In a library a person keeps the count, while in Swift the counting is built into the program itself. The compiler adds "add one" and "take one away" commands in advance to the lines where a reference appears or goes away. So while the program runs, nobody searches for unneeded objects: the count changes on each such line.
Interviewers like to ask how ARC differs from a garbage collector. In Java, C# and Go, memory is freed by a garbage collector. It is a part of the program that looks through memory from time to time and searches for objects that can no longer be reached. You can build the answer like this:
- ARC frees an object right away, on the line where the last strong reference went away;
- a garbage collector frees unneeded objects later, when its next pass gets to them;
- so with ARC the moment
deinitruns is predictable, and there are no passes over memory in the background.
Common mistake
A book was put into an array, then the variable was set to nil, and you wait for the line from deinit:
var hamlet: Book? = Book(title: "Hamlet")
var library = [Book]()
library.append(hamlet!)
hamlet = nil // ✗
print(library.count) // 1The line hamlet = nil prints nothing. An array element is a strong reference too, and the book has one left. deinit runs when the book is taken out of the array: library.remove(at: 0) prints Hamlet is back.
Key points
- ARC counts the strong references to every class object and frees the object as soon as the count reaches zero.
- Any strong reference holds an object: a variable, a property of another object, an array element.
deinitruns on the line where the last reference went away; if a reference lives to the end of the program, it does not run.- Unlike a garbage collector, ARC searches nothing in memory: the compiler builds the counting into the program.