Background:
Escape analysis is a term from compiler optimization and memory management. In Swift, it is important due to the active use of closures and ARC. It is related to the concepts of escaping and non-escaping closures, which determine whether closures can escape the scope of the function.
Problem:
Incorrectly determining the type of closure can lead to memory ownership errors, leaks, unexpected variable captures, and performance degradation. It's essential to clearly understand when a closure "escapes" and when it does not, and to mark them correctly.
Solution:
Swift requires explicitly marking escaping closures with the @escaping attribute. Non-escaping closures can only be captured within the function and automatically have more efficient memory management and can safely use captured variables.
Example of the difference:
// non-escaping closure (faster, not stored after call) func performSync(block: () -> Void) { block() } // escaping closure (can be stored and executed later) var storedCompletion: (() -> Void)? func performAsync(block: @escaping () -> Void) { storedCompletion = block }
Key features:
Can you change a closure defined as non-escaping to escaping without changing the original function code?
No. The @escaping attribute must be explicitly stated in the function signature. If a closure is passed outside of the function, only an escaping closure is required; otherwise, the compiler will raise an error.
Is it safe to pass self into an escaping closure without weak/unowned capture?
No. An escaping closure can potentially create a retain cycle if it captures self with a strong reference. You must manage the capture explicitly:
someAsync { [weak self] in self?.doSomething() }
Is an escaping closure always global (stored in memory until the end of the program)?
No. Its lifecycle depends on the scoping. If a closure is stored temporarily or its property is set to nil, it will be released as soon as the property loses its owner. Only closures that hold references to global objects or are stored in global variables become global.
Inside a class method, a closure is saved without @escaping (compiler error), later fixed but forgets about retain cycle protection — the application has a memory leak.
Pros:
Cons:
The developer always checks where an escaping closure is required, captures self as weak/unowned, avoids leaks, and works safely with memory.
Pros:
Cons: