Middle
Tell me about capture list in Swift.
sobes.tech AI
Answer from AI
The capture list in Swift is used for explicit management of variable capture by closures. It helps avoid strong reference cycles, especially when working with self inside closures used within classes. The main capture types are weak and unowned.
| Capture Type | Description | When to Use |
|---|---|---|
weak |
Captures the variable as an optional, without increasing the reference count. Its value can become nil. |
When the closure's object may outlive the captured object. |
unowned |
Captures the variable without increasing the reference count, but it is assumed that the captured object will be alive as long as the closure exists. Not optional. | When the closure and the captured object have the same lifetime, and the captured object will not be nil before the closure ends. |
Syntax of capture list:
{ [capture list] (parameters) -> returnType in
// code
}
Example of using weak self:
class MyClass {
var value = 0
func doSomethingLater() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
guard let self = self else { return } // nil check
self.value += 1
print("Value is now \(self.value)")
}
}
}
Example of using unowned self:
class ViewController: UIViewController {
var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
button = UIButton(type: .system)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
}
@objc func buttonTapped() {
// unowned self is not required here, as the button exists as long as the ViewController exists.
// But if the closure could outlive the ViewController, unowned might be appropriate
// if you are sure that self will not be nil.
let action = { [unowned self] in
self.view.backgroundColor = .blue
}
action()
}
}
You can capture not only self but also any other variables accessible within the closure. For example, to capture the value of a variable at the moment of closure creation:
func createIncrementer(initialValue: Int) -> () -> Int {
var currentValue = initialValue
return { [initialValue] in // Capture initialValue by value
currentValue += 1
print("Initial value was \(initialValue)") // initialValue retains its value
return currentValue
}
}
let incrementer = createIncrementer(initialValue: 5)
print(incrementer()) // Output: Initial value was 5, then 6
print(incrementer()) // Output: Initial value was 5, then 7