0Pricing
Swift Academy · Lesson

Capture Lists and Weak Self

Avoid retain cycles with [weak self].

Capture Lists and Weak Self is a free Swift Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

The Retain Cycle Risk

When an object stores an escaping closure that captures self strongly, each keeps the other alive forever — a retain cycle that leaks memory.

A Strong Cycle

The closure captures self strongly and self holds the closure — neither is ever freed:

class Owner {
    var onEvent: (() -> Void)?
    func setup() {
        onEvent = { print(self.describe()) }  // strong self -> cycle
    }
    func describe() -> String { "Owner" }
}
let o = Owner(); o.setup()
o.onEvent?()  // "Owner"  (but leaks)

Capture Lists

A capture list at the start of a closure { [weak self] in ... } controls how each captured reference is held: weak or unowned instead of strong.

Breaking the Cycle with weak self

[weak self] captures self as an optional that does not keep it alive:

class Owner {
    var onEvent: (() -> Void)?
    func setup() {
        onEvent = { [weak self] in
            print(self?.describe() ?? "gone")
        }
    }
    func describe() -> String { "Owner" }
}
let o = Owner(); o.setup()
o.onEvent?()  // "Owner"  (no cycle)

weak Makes self Optional

With [weak self], self becomes Self?, so you unwrap it:

class Worker {
    var tag = "W"
    lazy var job: () -> Void = { [weak self] in
        guard let self = self else { return }
        print(self.tag)
    }
}
let w = Worker()
w.job()  // "W"

The guard let self Pattern

Re-bind self early so the rest of the closure uses a strong, non-optional reference:

class Service {
    var name = "S"
    lazy var run: () -> Void = { [weak self] in
        guard let self else { return }
        print(self.name)
        print("still \(self.name)")
    }
}
Service().run()  // "S" / "still S"

weak vs unowned

weak yields an optional and is safe if the object may be gone. unowned is non-optional and faster but crashes if accessed after the object is deallocated — use only when self is guaranteed to outlive the closure.

Using unowned

Choose unowned when the closure cannot outlive self:

class Timer2 {
    var ticks = 0
    lazy var tick: () -> Void = { [unowned self] in
        self.ticks += 1
    }
}
let t = Timer2()
t.tick(); t.tick()
print(t.ticks)  // 2

Capturing Specific Properties

Capture only what you need instead of all of self — sometimes that avoids the cycle entirely:

class VM {
    let id = 42
    lazy var printer: () -> Void = { [id] in print(id) }
}
VM().printer()  // 42

Non-Escaping Needs No weak

Synchronous, non-escaping closures (like map) do not cause cycles, so capturing self strongly there is fine:

class Calc {
    let factor = 3
    func scale(_ xs: [Int]) -> [Int] {
        xs.map { $0 * self.factor }   // safe, non-escaping
    }
}
print(Calc().scale([1, 2]))  // [3, 6]

Rule of Thumb

Use [weak self] for stored/escaping closures that reference self. Reach for unowned only when self is guaranteed alive. Skip capture lists for non-escaping closures.

class P {
    var n = "P"
    var stored: (() -> Void)?
    func arm() { stored = { [weak self] in print(self?.n ?? "-") } }
}
let p = P(); p.arm(); p.stored?()  // "P"

Quick Check

Why use [weak self] in a stored escaping closure?

Recap

You learned capture lists:

  • Strong self in a stored escaping closure creates a retain cycle (leak)
  • [weak self] breaks it; self becomes optional — use guard let self
  • unowned is non-optional and faster but crashes if self is gone
  • Non-escaping closures do not need capture lists

Next: autoclosures.

Frequently asked questions

Is the “Capture Lists and Weak Self” lesson free?

Yes — the full text of “Capture Lists and Weak Self” is free to read here on the web, and the Swift Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Swift Academy course, upgrade to CoddyKit PRO.

What will I learn in “Capture Lists and Weak Self”?

Avoid retain cycles with [weak self]. You practise Swift Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Swift Academy?

No prior experience is required. Swift Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Capture Lists and Weak Self” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Swift Academy lesson?

Yes. Every Swift Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Capturing Values by Reference
  2. Escaping Closures
  3. Capture Lists and Weak Self
  4. Autoclosures
← Back to Swift Academy