← All topics/Swift language features

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.
  • @autoclosure defers evaluation of an argument, which is how assert avoids evaluating its condition in release builds.
Escaping vs non-escaping
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
}