Escaping closure is a closure that "escapes" the scope of the calling function (for example, it is saved for asynchronous execution).
By default, in Swift, functions take non-escaping closures: the closure is executed within the function call.
To explicitly indicate escaping, the @escaping keyword is used:
func asyncTask(completion: @escaping () -> Void) { DispatchQueue.main.async { completion() } }
Key differences:
[weak self] or [unowned self] to prevent memory leaks (retain cycle).Do you always need to write @escaping for closures passed as parameters to functions calling DispatchQueue.async?
— Yes. Since DispatchQueue.async retains the closure until it is executed, the closure becomes escaping.
Example:
func foo(action: () -> Void) { DispatchQueue.main.async { action() // Will not compile: closure must be escaping. } } // Needs to be: func bar(action: @escaping () -> Void) { ... }
Story
The controller created a strong reference to self inside an escaping closure (for example, a network request). The controller was not released after going off-screen — a strong retain cycle. Solution: use [weak self] or [unowned self].
Story
The function passed a closure to DispatchQueue.async, but did not mark it as escaping. The project did not compile, and errors were hard to find due to nested functions.
Story
Inside the closure, an object was accessed that had already been deinitialized by the time the closure was called (used [unowned self]). As a result — runtime crash. Solution: use [weak self] and nil checking.