@noescape attribute in Swift 1.2

Dániel Nagy picture Dániel Nagy · Feb 10, 2015 · Viewed 14.9k times · Source

There is a new attribute in Swift 1.2 with closure parameters in functions, and as the documentation says:

This indicates that the parameter is only ever called (or passed as an @ noescape parameter in a call), which means that it cannot outlive the lifetime of the call.

In my understanding, before that, we could use [weak self] to not let the closure to have a strong reference to e.g. its class, and self could be nil or the instance when the closure is executed, but now, @noescape means that the closure will never be executed if the class is deinitalized. Am I understand it correctly?

And if I'm correct, why would I use a @noescape closure insted of a regular function, when they behaves very similar?

Answer

rintaro picture rintaro · Feb 10, 2015

@noescape can be used like this:

func doIt(code: @noescape () -> ()) {
    /* what we CAN */

    // just call it
    code()
    // pass it to another function as another `@noescape` parameter
    doItMore(code)
    // capture it in another `@noescape` closure
    doItMore {
        code()
    }

    /* what we CANNOT do *****

    // pass it as a non-`@noescape` parameter
    dispatch_async(dispatch_get_main_queue(), code)
    // store it
    let _code:() -> () = code
    // capture it in another non-`@noescape` closure
    let __code = { code() }

    */
}

func doItMore(code: @noescape () -> ()) {}

Adding @noescape guarantees that the closure will not be stored somewhere, used at a later time, or used asynchronously.

From the caller's point of view, there is no need to care about the lifetime of captured variables, as they are used within the called function or not at all. And as a bonus, we can use an implicit self, saving us from typing self..

func doIt(code: @noescape () -> ()) {
    code()
}

class Bar {
    var i = 0
    func some() {
        doIt {
            println(i)
            //      ^ we don't need `self.` anymore!
        }
    }
}

let bar = Bar()
bar.some() // -> outputs 0

Also, from the compiler's point of view (as documented in release notes):

This enables some minor performance optimizations.