Closures in Swift are self-contained blocks of code that can capture and store references to variables and constants from their surrounding context. They allow for organizing callbacks, asynchronous processing, and storing code execution as variable values.
var counter = 0 let incrementer: () -> Void = { counter += 1 } incrementer() print(counter) // 1
[weak self], [unowned self]).self inside a closure.What is the difference between strong capture and using
[weak self]or[unowned self]in the capture list of a closure? What are the risks?
Answer:
If you do not specify [weak self] or [unowned self], the closure will by default capture self by strong reference, which will lead to a retain cycle if the closure and self reference each other. [weak self] captures self by weak reference (optional), and [unowned self] captures by unowned reference (non-optional). Improper choice or absence of a capture list leads to memory leaks or crashes.
class Test { var closure: (() -> Void)? func setup() { closure = { [weak self] in self?.doWork() } } func doWork() { ... } }
Story
A programmer did not use a capture list in a closure within a UIViewController that initiated asynchronous data loading. As a result, the controller and its view were not released, leading to a memory leak upon dismissal. An analysis of the infrastructure revealed hundreds of "stuck" controllers in memory.
Story
A closure captured self using [unowned self], but the lifecycle of self ended before the closure. This resulted in a crash when accessing an already released object — the application crashed when attempting to work with self inside the closure.
Story
In a project, within a nested closure, two variables were captured: one strongly, the other weakly. It turned out that the weak reference was nilled too early, and the data needed for the closure's operation was lost — the bug only manifested during a stress test under load in a multithreaded environment.