Question 5
Closures: captures and @escaping
What does @escaping mean for a closure parameter? When can a closure be implicitly non-escaping?
Answer outline
@escaping means the closure may outlive the call, because the callee stores it or runs it after returning.
Closure parameters are non-escaping by default, so the common case needs no annotation. A non-escaping closure runs during the call and cannot be stored for later. A related attribute, @autoclosure, wraps an argument in a closure so it's evaluated only if the callee uses it.
Escaping closures capture self strongly by default, so retain cycles become a design concern the moment a closure can outlive its owner.
Principles
- Non-escaping closures cannot be stored or run after the call returns, so they cannot create a retain cycle.
- Escaping closures need
[weak self]whenever they can outlive their owner and would otherwise retain it. @autoclosuredefers evaluation of an argument, which is howassertavoids evaluating its condition in release builds.
func load(completion: @escaping (Result<Data, Error>) -> Void) {
// Stored or invoked asynchronously later, so @escaping is required
}
func syncMap(_ x: Int, _ f: (Int) -> Int) -> Int {
f(x) // f runs before return, so it is non-escaping by default
}



