@escaping / @autoclosure / Capture List
In Swift, @escaping is an annotation for closures that outlive the function scope. @autoclosure automatically wraps an argument expression into a closure, and capture lists prevent retain cycles.
Syntax
// @escaping: closure stored or called after the function returns
func functionName(completion: @escaping () -> Void) {
DispatchQueue.main.async {
completion() // called after the function has returned
}
}
// @autoclosure: automatically wraps an argument into a closure
func functionName(_ condition: @autoclosure () -> Bool) { }
functionName(x > 0) // equivalent to { x > 0 }
// Capture list (prevents retain cycles)
{ [weak self, unowned obj] in
self?.method()
}
Syntax List
| Syntax | Description |
|---|---|
| @escaping | Indicates that the closure may be stored or called after the function returns. |
| @autoclosure | Automatically wraps the argument expression into a closure, enabling lazy evaluation. |
| [weak self] | Captures self as a weak reference. Use this when self may become nil. |
| [unowned self] | Captures self as an unowned reference. Use this only when self is guaranteed to be alive. |
| [weak obj] | Captures an arbitrary object as a weak reference. |
| self?.method() | Unwraps the weak self reference and calls a method on it. |
Sample Code
import Foundation
// @escaping example (callback for asynchronous processing)
var completionHandlers: [() -> Void] = []
func addHandler(_ handler: @escaping () -> Void) {
completionHandlers.append(handler) // stored outside the function scope
}
addHandler { print("Handler 1 executed") }
addHandler { print("Handler 2 executed") }
completionHandlers.forEach { $0() }
// @autoclosure example (enabling short-circuit evaluation)
func logIf(_ condition: @autoclosure () -> Bool, message: String) {
if condition() {
print("[LOG] \(message)")
}
}
let value = 42
logIf(value > 0, message: "Positive value") // value > 0 is wrapped into a closure
logIf(value < 0, message: "Negative value") // not evaluated if condition is false
// Preventing retain cycles with a capture list
class ViewController {
var name = "Main Screen"
func setupTimer() {
// Use [weak self] to prevent a retain cycle
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { [weak self] _ in
guard let self = self else { return }
print("\(self.name) timer fired")
}
}
}
// Capturing a value type (captured by copy)
var counter = 0
let captureByValue = { [counter] in
print("Value at capture time: \(counter)")
}
counter = 100
captureByValue() // prints the value at capture time (0)
print("Current value: \(counter)")
Notes
You must mark a closure with @escaping when it is stored as a property or called asynchronously. Omitting it causes a compile error.
When you use a closure inside a class method and reference self within the closure, be careful about retain cycles. Capture self with [weak self] and always unwrap it with self?. or guard let self = self. Use [unowned self] only when you can guarantee that self will always be alive.
For the basics of closures, see Closures (Basics) / { } / in.
If you find any errors or copyright issues, please contact us.