In Swift 3, the default attribute for closures is now @noescape (with the keyword being deprecated), we now have to specify closures that escape from a function as @escaping. There is a standard library function withoutAcutallyEscaping 1 that:

Allows a nonescaping closure to temporarily be used as if it were allowed to escape.


So what does it mean for a closure to “escape” from a function?

A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns.

  • Stored somewhere else
  • Used at a later time
  • Executed asynchronously

Here’s a sample of a closure that escapes from a function (in this case by executing asynchronously). This is a compile error:

func doSomething(_ f: ()->(Int)) { 
    // error: closure use of non-escaping parameter 'f' may allow it to escape
    DispatchQueue.main.async { print(f()) }
}
doSomething { return 3 }

There are however valid cases where you don’t want to specify @escaping in the function type signature because you know that the block doesn’t actually escape or get copied anywhere else. The Swift stdlib documentation gives an example where a function takes two closures and executes them concurrently 1:

func perform(_ f: () -> Void, simultaneouslyWith g: () -> Void) {
    let queue = DispatchQueue(label: "perform", attributes: .concurrent)
    queue.async(execute: f)
    queue.async(execute: g)
    queue.sync(flags: .barrier) {}
}
// error: passing non-escaping parameter 'f'...
// error: passing non-escaping parameter 'g'...

This is safe because the .barrier flag forces the function to wait until both closures have completed executing, so by using the withoutActuallyEscaping, we can keep the type signature for the function the same but still use the closures internally in an asynchronous manner:

func perform(_ f: () -> Void, simultaneouslyWith g: () -> Void) {
    withoutActuallyEscaping(f) { escapableF in
        withoutActuallyEscaping(g) { escapableG in
            let queue = DispatchQueue(label: "perform", attributes: .concurrent)
            queue.async(execute: escapableF)
            queue.async(execute: escapableG)
            queue.sync(flags: .barrier) {}
        }
    }
}