Skip to content
Theory · Memory and ARC · lesson 6 of 7

Closures and [weak self]

In the closures theme there was a recipe card with a note clipped to it: a closure carries along everything it captured. Now picture a library book with a borrower's card for that very book in its pocket. While the card is in the pocket, the book counts as checked out. And to take the card out, the book first has to go back on the shelf.

This happens when an object stores a closure, and the closure uses that same object through self:

SWIFT
class Player {
    let name = "Anna"
    var onFinish: () -> Void = {}
    init() {
        onFinish = { print("\(self.name) finished") } // ✗
    }
    deinit { print("Player freed") }
}

var player: Player? = Player()
player?.onFinish() // Anna finished
player = nil

Let's go through the program:

  1. onFinish is a property that stores a closure with no parameters and no result, hence the type () -> Void. At first it holds the empty closure {}.
  2. In init, a new closure goes into onFinish. It uses self.name and so captures self, that is, the object itself, with a strong reference.
  3. player?.onFinish() calls the closure, and the program prints Anna finished.
  4. player = nil removes the outside reference, but there is no Player freed line: deinit did not run.

This is a strong reference cycle. The object holds the closure through the onFinish property, and the closure holds the object through the captured self. The second member of the cycle here is not an object but a closure.

Here the pocket picture is off: you see a card in a pocket right away, while the capture hides in the text of the closure. To make it visible, Swift requires you to write self. explicitly. Without it, the line print("\(name) finished") does not compile:

TEXT
error: reference to property 'name' in closure requires explicit use of 'self' to make capture semantics explicit

The capture list

A cycle is broken the same way as between two objects: one of the references becomes weak. The onFinish property has to hold its closure, so the closure's reference to the object is the one made weak. That is what the capture list is for — square brackets before the word in that say how values are captured:

SWIFT
class Player {
    let name = "Anna"
    var onFinish: () -> Void = {}
    init() {
        onFinish = { [weak self] in
            guard let self else { return }
            print("\(self.name) finished")
        }
    }
    deinit { print("Player freed") }
}

var player: Player? = Player()
player?.onFinish() // Anna finished
player = nil // Player freed

Let's see what changed:

  1. [weak self] captures the object as a weak reference: the closure knows the object but does not hold it.
  2. Inside the closure, self is now an optional Player?: by the time of the call, the object may be gone.
  3. guard let self else { return } is the short form of guard let self = self, the same as if let score. If the object is alive, below it self is a plain Player. If not, the closure leaves right away.
  4. player = nil removes the only strong reference, and the program prints Player freed.

When the object is already gone

A closure can outlive its object, for example when it was saved in another constant:

SWIFT
var guest: Player? = Player()
let finish = guest!.onFinish
guest = nil // Player freed
finish()

The constant finish holds the closure itself, but not the object. So guest = nil frees the object, and by the call finish(), self inside the closure is already nil. guard leaves, and the program prints nothing.

Other forms

Through self?.

Instead of guard let self, you can reach the object through ?., as with any optional: print(self?.name ?? "nobody"). If the object is already gone, such a closure prints nobody.

[unowned self]

[unowned self] does not hold the object either, and self inside the closure is not an optional. But if you call such a closure after the object is freed, the program crashes, as with any unowned reference. That is why [weak self] is written more often.

Interviewers like to ask this: why do you need [weak self]? You can build the answer like this:

  • an object that stores a closure holds it with a strong reference;
  • a closure that uses self holds the object too, and that makes a strong reference cycle;
  • [weak self] captures the object as a weak reference, so there is no cycle;
  • inside the closure, self becomes an optional, and you check it with guard let self.

Common mistake

You wrote [weak self] but access the property as before:

SWIFT
class Coach {
    let name = "Boris"
    var onStart: () -> Void = {}
    init() {
        onStart = { [weak self] in
            print(self.name) // ✗
        }
    }
}
TEXT
error: value of optional type 'Coach?' must be unwrapped to refer to member 'name' of wrapped base type 'Coach'

The program does not compile: after [weak self], self inside the closure is an optional Coach?, and its properties cannot be read directly. Add guard let self else { return } at the start of the closure, or write print(self?.name ?? "nobody").

Key points

  • An object that stores a closure and a closure that uses self hold each other: that is a strong reference cycle.
  • Swift requires self. to be written explicitly in such a closure, so the capture is visible.
  • [weak self] in the capture list captures the object as a weak reference; inside, self is an optional, checked with guard let self.
  • If the object is already gone, self in the closure is nil and the call is safe, while with [unowned self] the program crashes.