Lesson 11 / 25
Closures and Capture Lists
Write closures with shorthand syntax, trailing closures and capture lists, and know @escaping.
Functions without names
Closures are self-contained blocks of functionality, like lambdas in other languages: { (a: Int, b: Int) -> Bool in a < b }. Swift lets you shorten them heavily: inferred types, implicit returns, shorthand arguments ($0, $1) and trailing closure syntax when the closure is the last argument (orders.sorted { $0.total > $1.total }), with multiple trailing closures for APIs such as animations. Closures capture variables from their surrounding scope by reference. A closure passed to a function that stores it or calls it later must be marked @escaping by that function, while non-escaping closures (the default) are called before the function returns. Escaping closures that capture self in a class can create retain cycles, so use a capture list such as [weak self] (then guard let self else { return }) or [unowned self] when you are certain of lifetimes. Capture lists can also capture values at creation time: [count] copies the current value instead of referencing the variable.
Closure syntax and capture lists
From full syntax to shorthand, and weak self in an escaping closure.
let prices = [499.0, 120.0, 899.0]
let sortedFull = prices.sorted(by: { (a: Double, b: Double) -> Bool in return a < b })
let sortedShort = prices.sorted { $0 < $1 } // trailing closure + shorthand
let withTax = prices.map { $0 * 1.18 }
func makeCounter() -> () -> Int {
var count = 0
return { count += 1; return count } // captures count by reference
}
let next = makeCounter()
_ = next() // 1
_ = next() // 2
final class ImageLoader {
private var cache: [URL: Data] = [:]
func load(_ url: URL, completion: @escaping (Data?) -> Void) {
URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
guard let self else { return } // loader may already be gone
if let data { self.cache[url] = data }
completion(data)
}.resume()
}
}A note that remembers the room
A closure is a note written in a room that still remembers the room's contents. If the note is pinned to the room's own wall and the room keeps the note, neither can ever be thrown away: that is a retain cycle, and [weak self] breaks it.
Quick check: When must a closure parameter be marked @escaping?
- Always
- When the function stores the closure or calls it after returning
- When the closure returns a value
- When it uses $0
Answer
When the function stores the closure or calls it after returning — Escaping closures outlive the function call, which affects memory management and capturing.