Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A capture list is a Swift language construct used within a closure to explicitly define the memory management rules and evaluation timing for variables and constants captured from the surrounding lexical scope. It dictates whether a captured reference is strong, weak, or unowned, and forces variables to be evaluated and captured at the exact moment of closure creation rather than at execution.

Syntax

The capture list is written as a comma-separated list of variables enclosed in square brackets []. It is placed at the beginning of the closure body, immediately following any closure attributes (such as @MainActor or @Sendable), and preceding any parameters, return type declarations, and the in keyword.
{ @MainActor [modifier variable1, modifier variable2] (parameter) -> ReturnType in
    // Closure body
}
If the closure relies on implicit parameters and return types, the in keyword must still be included when a capture list is present:
{ [modifier variable] in
    // Closure body
}

Mechanics of Capture

By default, Swift closures capture variables from their surrounding scope by reference. This means the closure holds a reference to the original variable, allowing it to observe modifications made outside the closure and vice versa. Introducing a capture list fundamentally changes this behavior depending on the type of the captured entity.

1. Value Types

When a value type (e.g., Int, String, Struct) is included in a capture list, Swift evaluates the variable at the time the closure is created and captures a constant copy of that value.
var counter = 0

// Default capture (by reference)
let defaultClosure = {
    print(counter) 
}

// Capture list (by value at creation time)
let captureListClosure = { [counter] in
    print(counter) 
}

counter = 10

defaultClosure()       // Prints: 10
captureListClosure()   // Prints: 0
Note: Variables captured this way are immutable within the closure. Attempting to mutate counter inside captureListClosure will result in a compiler error.

2. Reference Types

When a reference type (e.g., Class instance) is included in a capture list, the closure still captures a reference to the object, but the capture list allows you to modify the Automatic Reference Counting (ARC) semantics. The modifiers applied in the capture list dictate the strength of the reference:
  • Implicit (Strong): If no modifier is provided (e.g., [self]), the closure maintains a strong reference to the instance, incrementing its retain count.
  • weak: The closure captures a weak reference. The retain count is not incremented. Because the instance can be deallocated while the closure is still alive, the captured reference is automatically wrapped in an Optional type inside the closure.
  • unowned: The closure captures an unowned reference. The retain count is not incremented. Unlike weak, unowned does not wrap the reference in an additional Optional; it preserves the exact existing type of the variable. If the captured variable is already an Optional, the unowned reference remains an Optional. It assumes the captured instance will outlive the closure. Accessing an unowned reference after the underlying instance has been deallocated triggers a runtime crash.
{ [weak self, unowned delegate = self.delegate] in
    // 'self' is wrapped in an Optional (e.g., MyClass?)
    // 'delegate' retains its original type (e.g., DelegateType?)
}

Variable Binding and Aliasing

Capture lists support arbitrary expression evaluation and aliasing. You can declare new local variables within the capture list that are scoped exclusively to the closure. These expressions are evaluated exactly once, at closure creation.
{ [newVariable = someObject.property, unwrappedValue = someOptional ?? defaultValue] in
    // newVariable and unwrappedValue are available here
}
This mechanism is frequently used to capture a specific property of an object without capturing the entire object, or to safely provide default fallback values for optionals at the boundary of the closure scope.
Master Swift with Deep Grasping Methodology!Learn More