Swift Language Pasando cierres a funciones


Ejemplo

Las funciones pueden aceptar cierres (u otras funciones) como parámetros:

func foo(value: Double, block: () -> Void) { ... }
func foo(value: Double, block: Int -> Int) { ... }
func foo(value: Double, block: (Int, Int) -> String) { ... }

Sintaxis de cierre de seguimiento

Si el último parámetro de una función es un cierre, las llaves de cierre { / } se pueden escribir después de la invocación de la función:

foo(3.5, block: { print("Hello") })

foo(3.5) { print("Hello") }

dispatch_async(dispatch_get_main_queue(), {
    print("Hello from the main queue")
})

dispatch_async(dispatch_get_main_queue()) {
    print("Hello from the main queue")
}

Si el único argumento de una función es un cierre, también puede omitir el par de paréntesis () al llamar con la sintaxis de cierre final:

func bar(block: () -> Void) { ... }

bar() { print("Hello") }

bar { print("Hello") }

Parámetros de @noescape

Los parámetros de cierre marcados con @noescape están garantizados para ejecutarse antes de que se devuelva la llamada a la función, por lo que se usa self. No se requiere dentro del cuerpo de cierre:

func executeNow(@noescape block: () -> Void) {
    // Since `block` is @noescape, it's illegal to store it to an external variable.
    // We can only call it right here.
    block()
}

func executeLater(block: () -> Void) {
    dispatch_async(dispatch_get_main_queue()) {
        // Some time in the future...
        block()
    }
}
class MyClass {
    var x = 0
    func showExamples() {
        // error: reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit
        executeLater { x = 1 }

        executeLater { self.x = 2 }  // ok, the closure explicitly captures self

        // Here "self." is not required, because executeNow() takes a @noescape block.
        executeNow { x = 3 }

        // Again, self. is not required, because map() uses @noescape.
        [1, 2, 3].map { $0 + x }
    }
}

Nota de Swift 3:

Tenga en cuenta que en Swift 3, ya no marca bloques como @noescape. Los bloques ahora no se escapan por defecto. En Swift 3, en lugar de marcar un cierre como sin escape, se marca un parámetro de función que es un cierre con escape mediante la palabra clave "@escaping".


throws y rethrows

Los cierres, como otras funciones, pueden arrojar errores :

func executeNowOrIgnoreError(block: () throws -> Void) {
    do {
        try block()
    } catch {
        print("error: \(error)")
    }
}

La función puede, por supuesto, pasar el error a su interlocutor:

func executeNowOrThrow(block: () throws -> Void) throws {
    try block()
}

Sin embargo, si el bloque que se pasa no se lanza, la persona que llama sigue con la función de lanzar:

// It's annoying that this requires "try", because "print()" can't throw!
try executeNowOrThrow { print("Just printing, no errors here!") }

La solución es rethrows , lo que designa que la función solo se puede lanzar si su parámetro de cierre se lanza :

func executeNowOrRethrow(block: () throws -> Void) rethrows {
    try block()
}

// "try" is not required here, because the block can't throw an error.
executeNowOrRethrow { print("No errors are thrown from this closure") }

// This block can throw an error, so "try" is required.
try executeNowOrRethrow { throw MyError.Example }

Muchas funciones de biblioteca estándar utilizan rethrows , incluidos map() , filter() e indexOf() .