Swift Cierres

Los cierres (Closures) son bloques de código funcionales autónomos que se pueden usar en el código o pasar como parámetros para transferir valores.

Los cierres en Swift son similares a los bloques (blocks) en C y Objective-C, y a las funciones anónimas en algunos otros lenguajes de programación.

Las funciones globales y las funciones anidadas son en realidad cierres especiales.

Las formas de los cierres son:

Funciones globales Funciones anidadas Expresiones de cierre
Tienen nombre pero no pueden capturar ningún valor. Tienen nombre y también pueden capturar valores dentro de la función que los contiene. Cierres sin nombre que utilizan una sintaxis ligera y pueden capturar valores según el contexto.

Los cierres en Swift tienen muchas optimizaciones:

  1. Inferir tipos de parámetros y valores de retorno según el contexto
  2. Retorno implícito de cierres de expresión de una sola línea (es decir, si el cuerpo del cierre tiene solo una línea de código, se puede omitir return)
  3. Se pueden usar nombres de parámetros abreviados, como $0, $1 (comenzando desde 0, representan el i-ésimo parámetro...)
  4. Proporciona la sintaxis de cierre final (Trailing closure syntax)
  5. Sintaxis

    A continuación se define una sintaxis de cierre que recibe parámetros y devuelve un tipo especificado:

    {(parameters) -> return type in
       statements
    }
    

    Ejemplo

    import Cocoa
    
    let studname = { print("Swift 闭包实例。") }
    studname()
    

    El resultado de ejecutar el programa anterior es:

    Swift 闭包实例。
    

    La siguiente forma de cierre recibe dos parámetros y devuelve un valor booleano:

    {(Int, Int) -> Bool in
       Statement1
       Statement 2
        ---
       Statement n
    }
    

    Ejemplo

    import Cocoa
    
    let divide = {(val1: Int, val2: Int) -> Int in 
       return val1 / val2 
    }
    let result = divide(200, 20)
    print (result)
    

    El resultado de ejecutar el programa anterior es:

    10
    

    Expresión de cierre

    La expresión de cierre es una forma de construir cierres en línea utilizando una sintaxis concisa. Las expresiones de cierre proporcionan algunas optimizaciones de sintaxis que hacen que escribir cierres sea simple y claro.


    Método sorted

    La biblioteca estándar de Swift proporciona un método llamadosorted(by:)que ordena los valores de un array de tipo conocido según la función de cierre de ordenación que usted proporcione.

    Una vez completada la ordenación, el método sorted(by:) devuelve un nuevo array del mismo tamaño que el array original, que contiene elementos del mismo tipo y ya correctamente ordenados. El array original no se modifica con el método sorted(by:).

    El método sorted(by:) requiere dos parámetros:

    • Un array de tipo conocido
    • Una función de cierre, que debe recibir dos valores del mismo tipo que los elementos del array y devolver un valor booleano que indique si, al finalizar la ordenación, el primer parámetro aparece antes o después que el segundo parámetro. Si el valor del primer parámetro aparece antes que el valor del segundo parámetro, la función de cierre de ordenación debe devolvertrue, de lo contrario devuelvefalse。

    Ejemplo

    import Cocoa
    
    let names = ["AT", "AE", "D", "S", "BE"]
    
    // 使用普通函数(或内嵌函数)提供排序功能,闭包函数类型需为(String, String) -> Bool.
    func backwards(s1: String, s2: String) -> Bool {
        return s1 > s2
    }
    var reversed = names.sorted(by: backwards)
    
    print(reversed)
    

    El resultado de ejecutar el programa anterior es:

    ["S", "D", "BE", "AT", "AE"]
    

    Si la primera cadena (s1) es mayor que la segunda cadena (s2), la función backwards devuelve true, lo que indica que en el nuevo array s1 debe aparecer antes que s2. Para los caracteres de una cadena, "mayor" significa "aparece más tarde según el orden alfabético". Esto significa que la letra "B" es mayor que la letra "A", y la cadena "S" es mayor que la cadena "D". Esto realizará una ordenación alfabética inversa, de modo que "AT" se colocará antes que "AE".


    Abreviaturas de nombres de parámetros

    Swift proporciona automáticamente la función de abreviación de nombres de parámetros para funciones en línea; puede llamar secuencialmente a los parámetros del cierre directamente mediante $0, $1, $2.

    Ejemplo

    import Cocoa
    
    let names = ["AT", "AE", "D", "S", "BE"]
    
    var reversed = names.sorted( by: { $0 > $1 } )
    print(reversed)
    

    $0 y $1 representan el primer y segundo parámetro de tipo String del cierre.

    El resultado de ejecutar el programa anterior es:

    ["S", "D", "BE", "AT", "AE"]
    

    Si utiliza abreviaturas de nombres de parámetros en una expresión de cierre, puede omitir su definición en la lista de parámetros del cierre, y los tipos correspondientes a las abreviaturas de nombres de parámetros se inferirán a través del tipo de función. La palabra clave in también se puede omitir.


    Función de operador

    En realidad, hay una forma aún más corta de escribir la expresión de cierre del ejemplo anterior.

    El tipoStringde Swift define una implementación de cadena para el operador mayor que (>), que como función acepta dosStringparámetros de tipo y devuelveBoolun valor de tipo.sort(_:)Y esto coincide exactamente con el tipo de función requerido por el segundo parámetro del método. Por lo tanto, puede simplemente pasar un operador mayor que, y Swift puede inferir automáticamente que desea utilizar la implementación de la función de cadena del operador mayor que:

    import Cocoa
    
    let names = ["AT", "AE", "D", "S", "BE"]
    
    var reversed = names.sorted(by: >)
    print(reversed)
    

    El resultado de ejecutar el programa anterior es:

    ["S", "D", "BE", "AT", "AE"]
    

    Cierre final

    Un cierre final es una expresión de cierre escrita después de los paréntesis de la función, y la función admite llamarlo como último parámetro.

    func someFunctionThatTakesAClosure(closure: () -> Void) {
        // 函数体部分
    }
    
    // 以下是不使用尾随闭包进行函数调用
    someFunctionThatTakesAClosure({
        // 闭包主体部分
    })
    
    // 以下是使用尾随闭包进行函数调用
    someFunctionThatTakesAClosure() {
      // 闭包主体部分
    }
    

    Ejemplo

    import Cocoa
    
    let names = ["AT", "AE", "D", "S", "BE"]
    
    //尾随闭包
    var reversed = names.sorted() { $0 > $1 }
    print(reversed)
    

    El { $0 > $1} después de sort() es un cierre final.

    El resultado de ejecutar el programa anterior es:

    ["S", "D", "BE", "AT", "AE"]
    

    Nota: Si la función solo necesita un parámetro de expresión de cierre, cuando use un cierre final, incluso puede omitir()omitirlo.

    reversed = names.sorted { $0 > $1 }
    

    Capturar valores

    Un cierre puede capturar constantes o variables en el contexto en el que se define.

    Incluso si el ámbito original donde se definieron estas constantes y variables ya no existe, el cierre puede seguir refiriéndose a estos valores y modificándolos dentro del cuerpo de la función de cierre.

    La forma más simple de cierre en Swift es la función anidada, es decir, una función definida dentro del cuerpo de otra función.

    Las funciones anidadas pueden capturar todos los parámetros de su función externa, así como las constantes y variables definidas.

    Veamos este ejemplo:

    func makeIncrementor(forIncrement amount: Int) -> () -> Int {
        var runningTotal = 0
        func incrementor() -> Int {
            runningTotal += amount
            return runningTotal
        }
        return incrementor
    }
    

    Una función makeIncrementor tiene un parámetro de tipo Int llamado amout, y tiene un nombre de parámetro externo forIncremet, lo que significa que al llamarla debe usar este nombre externo. El valor de retorno es una()-> Intfunción.

    Dentro del cuerpo de la función, se declaran la variable runningTotal y una función incrementor.

    La función incrementor no recibe ningún parámetro, pero dentro del cuerpo de la función accede a las variables runningTotal y amount. Esto se debe a que lo logra capturando las variables runningTotal y amount que ya existen en el cuerpo de la función que la contiene.

    Dado que la variable amount no se modifica, incrementor en realidad captura y almacena una copia de dicha variable, y esa copia se almacena junto con incrementor.

    Por lo tanto, cuando llamamos a esta función, se va acumulando:

    import Cocoa
    
    func makeIncrementor(forIncrement amount: Int) -> () -> Int {
        var runningTotal = 0
        func incrementor() -> Int {
            runningTotal += amount
            return runningTotal
        }
        return incrementor
    }
    
    let incrementByTen = makeIncrementor(forIncrement: 10)
    
    // 返回的值为10
    print(incrementByTen())
    
    // 返回的值为20
    print(incrementByTen())
    
    // 返回的值为30
    print(incrementByTen())
    

    El resultado de ejecutar el programa anterior es:

    10
    20
    30
    

    Los cierres son tipos de referencia

    En el ejemplo anterior, incrementByTen es una constante, pero el cierre al que apuntan estas constantes aún puede incrementar el valor de la variable capturada.

    Esto se debe a que tanto las funciones como los cierres son tipos de referencia.

    Ya sea que asigne la función/cierre a una constante o a una variable, en realidad está estableciendo el valor de la constante/variable como la referencia a la función/cierre correspondiente. En el ejemplo anterior, la referencia de incrementByTen al cierre es una constante, y no el contenido del cierre en sí.

    Esto también significa que si asigna el cierre a dos constantes/variables diferentes, ambos valores apuntarán al mismo cierre:

    import Cocoa
    
    func makeIncrementor(forIncrement amount: Int) -> () -> Int {
        var runningTotal = 0
        func incrementor() -> Int {
            runningTotal += amount
            return runningTotal
        }
        return incrementor
    }
    
    let incrementByTen = makeIncrementor(forIncrement: 10)
    
    // 返回的值为10
    incrementByTen()
    
    // 返回的值为20
    incrementByTen()
    
    // 返回的值为30
    incrementByTen()
    
    // 返回的值为40
    incrementByTen()
    
    let alsoIncrementByTen = incrementByTen
    
    // 返回的值也为50
    print(alsoIncrementByTen())
    

    El resultado de ejecutar el programa anterior es:

    50
    Otras extensiones