weak and unowned
In the library, a borrower's card holds a book: while the card is out, the book does not go back to the shelf. But some records hold nothing. The library catalog shows who has a book right now. When the book is returned, the catalog erases that record by itself.
In the previous lesson, the person and the apartment held each other, and both objects leaked. To avoid the cycle, one of the two references has to be such a catalog record: it knows about the object but does not hold it.
class Person {
var home: Apartment?
deinit { print("Person freed") }
}
class Apartment {
weak var tenant: Person?
}
var anna: Person? = Person()
let flat = Apartment()
anna?.home = flat
flat.tenant = anna
anna = nil // Person freed
print(flat.tenant == nil) // trueLet's go through the program:
weak var tenant— the apartment knows its tenant but does not hold them: this reference adds nothing to the count ofPerson.- After
flat.tenant = anna, the person has one strong reference —anna. anna = nilremoves it. The count is zero,deinitruns, and the program printsPerson freed.- Swift writes
nilintoflat.tenantby itself, soprintprintstrue.
There is no cycle any more: the person holds the apartment, and the apartment does not hold the person.
A weak reference
A weak reference is a reference that does not hold its object: it adds nothing to the object's count. When the object is freed, Swift writes nil into it by itself.
How you work with one:
- you write the word
weakbefore the declaration:weak var tenant: Person?; - its type is always optional, because
nilhas to fit in; - you read it like any optional: with
if let,?.or a comparison withnil.
Without ?, the compiler rejects the declaration:
error: 'weak' variable should have optional type 'Person?'That is why a weak reference is safe: if the object is already gone, you read nil.
Which reference to make weak
Of the two references in a cycle, one becomes weak, and the choice is not random. Ask yourself who owns whom:
- the strong reference goes from the owner to what it owns: the person holds their apartment;
- the reference back, to the owner, becomes
weak: the apartment only knows its tenant.
A parent–child pair works the same way: the parent holds the child with a strong reference, and the child knows its parent through weak. If you do it the other way round, nothing holds the child, and it is freed too early.
An unowned reference
Sometimes an object is sure to live longer than the reference to it. A bank card always has an owner, and the card does not exist without one. For such a reference there is a second kind — an unowned reference. It does not hold the object either, but Swift never writes nil into it, so it is usually declared without ?.
class Customer {
let name: String
init(name: String) { self.name = name }
}
class Card {
unowned let owner: Customer
init(owner: Customer) { self.owner = owner }
}
var alice: Customer? = Customer(name: "Alice")
let card = Card(owner: alice!)
print(card.owner.name) // AliceLet's see what happens here:
unowned let owner: Customer— the card knows its owner but does not hold them.- The type has no
?, socard.owner.nameis read directly, withoutif letor?.. - The variable
aliceholds the owner, and while it does, the card works.
unowned is handier, but riskier. If the owner was freed before the card after all, the reference must not be read:
alice = nil
print(card.owner.name) // ✗The program crashes: Swift reports that an unowned reference to an object that was already destroyed was read. So unowned is used only when you are sure the object outlives the reference. If in doubt, use weak.
Here the catalog picture ends. weak is like a catalog record that is erased when the book comes back. And unowned is a slip of paper nobody erases: if the book has already been returned, following the slip means crashing the program.
Interviewers like to ask how weak differs from unowned. You can build the answer like this:
- neither reference holds the object, so both break a cycle;
weakis an optional: once the object is freed, it holdsnil, and reading it is safe;unownednever turns intonil, and reading it after the object is freed crashes the program;unownedis used only when the object is sure to live longer than the reference.
Common mistake
A new object is put straight into a weak reference:
let studio = Apartment()
studio.tenant = Person() // ✗
print(studio.tenant == nil) // truewarning: instance will be immediately deallocated because property 'tenant' is 'weak'The compiler warns you: the new object has no strong reference at all, and weak does not hold it. The object is freed on the same line — deinit prints Person freed — and tenant becomes nil at once. First put the object into a strong reference, such as let carl = Person(), and only then into studio.tenant.
Key points
weakandunowneddo not hold the object: they add nothing to its count, so they break a cycle.- A
weakreference is optional: once the object is freed, Swift writesnilinto it. - The reference back, to the owner, becomes
weak: the child knows its parent throughweak, and the parent holds the child with a strong reference. unownednever becomesnil: reading it after the object is freed crashes the program, so it is used only when the object lives longer than the reference.