Si ha trabajado con bloques en C o Objective-C o lambdas en Ruby, entonces no tendrá dificultades para comprender el concepto de cierres. Los cierres no son más que bloques de funcionalidad que puede transmitir en su código.
De hecho, ya hemos trabajado con cierres en las lecciones anteriores. Así es: las funciones también son cierres. Comencemos con lo básico e inspeccionemos la anatomía de un cierre..
Como dije, un cierre es un bloque de funcionalidad que puede pasar en su código. Puede pasar un cierre como un argumento de una función o puede almacenarlo como una propiedad de un objeto. Los cierres tienen muchos casos de uso..
El nombre cierre insinúa una de las características clave de los cierres. Un cierre captura las variables y constantes del contexto en el que se define. Esto a veces se conoce como cerrando sobre Esas variables y constantes. Vamos a ver la captura de valor con más detalle al final de esta lección..
Ya has aprendido que las funciones pueden ser increíblemente poderosas y flexibles. Debido a que las funciones son cierres, los cierres son igual de flexibles. En este artículo, descubres lo flexibles y poderosos que son.
El lenguaje de programación C tiene un concepto similar., bloques. Los cierres en Swift, sin embargo, tienen algunos beneficios. Una de las ventajas clave de los cierres en Swift es que la administración de la memoria es algo que usted, el desarrollador, no debe preocuparse.
Incluso los ciclos de retención, que no son infrecuentes en C u Objective-C, son manejados por Swift. Esto reduce las pérdidas de memoria difíciles de encontrar o los bloqueos causados por punteros no válidos.
La sintaxis básica de un cierre no es difícil, y puede recordarle funciones globales y anidadas, que hemos cubierto anteriormente en esta serie. Echa un vistazo al siguiente ejemplo..
(a: Int) -> Int en retorno a + 1
Lo primero que notará es que todo el cierre está envuelto en un par de llaves. Los parámetros del cierre están envueltos en un par de paréntesis, separados del tipo de retorno por el ->
símbolo. El cierre anterior acepta un argumento., una
, de tipo En t
, y devuelve un En t
. El cuerpo del cierre comienza después de la en
palabra clave.
Los cierres con nombre, que son funciones globales y anidadas, se ven un poco diferentes. El siguiente ejemplo debe ilustrar las diferencias..
func increment (_ a: Int) -> Int return a + 1
Las diferencias más destacadas son el uso del función
palabra clave y la posición de los parámetros y el tipo de retorno. Un cierre comienza y termina con un refuerzo rizado, envolviendo los parámetros, el tipo de retorno y el cuerpo del cierre. A pesar de estas diferencias, recuerde que cada función es un cierre. Sin embargo, no todo cierre es una función.
Los cierres son poderosos, y el siguiente ejemplo ilustra cuán útiles pueden ser. En el ejemplo, creamos una matriz de estados. Invocamos el mapa(_:)
funciona en la matriz para crear una nueva matriz que solo contiene las dos primeras letras de cada estado como una cadena en mayúscula.
var states = ["California", "New York", "Texas", "Alaska"] let abbreviatedStates = states.map ((state: String) -> String in let index = state.index (state.startIndex, offsetBy : 2) return state.substring (to: index) .uppercased ()) print (abreviados Estados)
los mapa(_:)
La función o el método es común a muchos lenguajes de programación y bibliotecas, como Ruby, PHP y JavaScript. En el ejemplo anterior, el mapa(_:)
la función se invoca en el estados
matriz, transforma su contenido y devuelve una nueva matriz que contiene los valores transformados. No te preocupes por el cuerpo del cierre por ahora..
Anteriormente en esta serie, aprendimos que Swift es bastante inteligente. Déjame mostrarte exactamente lo inteligente. La matriz de estados es una matriz de cadenas. Porque invocamos el mapa(_:)
función en la matriz, Swift sabe que la estado
el argumento es de tipo Cuerda
. Esto significa que podemos omitir el tipo, como se muestra en el siguiente ejemplo actualizado.
let abbreviatedStates = states.map ((state) -> String in let index = state.index (state.startIndex, offsetBy: 2) devuelve state.substring (a: index) .uppercased ())
Hay algunas cosas más que podemos omitir en el ejemplo anterior, lo que da como resultado lo siguiente:.
let abbreviatedStates = states.map (state en state.substring (to: state.index (state.startIndex, offsetBy: 2)). uppercased ())
Déjame explicarte lo que está pasando.
El compilador puede inferir que devolvemos una cadena desde el cierre que pasamos a la mapa(_:)
función, lo que significa que no hay razón para incluirla en la definición de expresión de cierre.
Sin embargo, solo podemos hacer esto si el cuerpo del cierre incluye una sola declaración. En ese caso, podemos poner esa declaración en la misma línea que la definición de cierre, como se muestra en el ejemplo anterior. Porque no hay tipo de retorno en la definición y no hay ->
símbolo que precede al tipo de retorno, podemos omitir los paréntesis que encierran los parámetros del cierre.
Aunque no se detiene aquí. Podemos utilizar nombres de argumentos abreviados para simplificar aún más la expresión de cierre anterior. Cuando se utiliza una expresión de cierre en línea, como en el ejemplo anterior, podemos omitir la lista de parámetros, incluida la en
Palabra clave que separa los parámetros del cuerpo de cierre..
En el cuerpo de cierre, hacemos referencia a los argumentos que usan nombres de argumentos abreviados que Swift nos proporciona. El primer argumento es referenciado por $ 0
, el segundo por $ 1
, etc.
En el ejemplo actualizado a continuación, he omitido la lista de parámetros y la en
palabra clave, y reemplazó el estado
Argumento en el cuerpo del cierre con el nombre abreviado del argumento. $ 0
. La declaración resultante es más concisa sin comprometer la legibilidad.
let abbreviatedStates = states.map ($ 0.substring (to: $ 0.index ($ 0.startIndex, offsetBy: 2)). uppercased ())
El lenguaje de programación Swift también define un concepto conocido como cierres finales. La idea es simple. Si pasa un cierre como último argumento de una función, puede colocar ese cierre fuera de los paréntesis de la llamada de función. El siguiente ejemplo demuestra cómo funciona esto.
let abbreviatedStates = states.map () $ 0.substring (to: $ 0.index ($ 0.startIndex, offsetBy: 2)). uppercased ()
Si el único argumento de la llamada de función es el cierre, incluso es posible omitir los paréntesis de la llamada de función.
deje que abbreviatedStates = states.map $ 0.substring (to: $ 0.index ($ 0.startIndex, offsetBy: 2)). uppercased ()
Tenga en cuenta que esto también funciona para cierres que contienen varias declaraciones. De hecho, esa es la razón principal por la que los cierres finales están disponibles en Swift. Si un cierre es largo o complejo y es el último argumento de una función, a menudo es mejor usar la sintaxis de cierre final..
let abbreviatedStates = states.map (state) -> String in let index = state.index (state.startIndex, offsetBy: 2) devuelve state.substring (a: index) .uppercased ()
Al usar cierres, a menudo se encontrará usando o manipulando constantes y variables del contexto circundante del cierre en el cuerpo del cierre. Esto a menudo se conoce como captura de valor. Simplemente significa que un cierre puede capturar los valores de constantes y variables del contexto en el que se define. Tome el siguiente ejemplo para entender mejor el concepto de captura de valor.
func changeCase (mayúsculas: Bool, ofStrings strings: String…) -> [String] var newStrings = [String] () func changeToUppercase () para s en strings newStrings.append (s.uppercased ()) func changeToLowerCase () para s en cadenas newStrings.append (s.lowercased ()) if mayúsculas changeToUppercase () else changeToLowerCase () devolver newStrings: "," Nueva York ") permite que lowercasedStates = changeCase (en mayúsculas: false, ofStrings:" California "," New York ")
Estoy seguro de que está de acuerdo en que el ejemplo anterior es un poco artificial, pero muestra claramente cómo funciona la captura de valor en Swift. Las funciones anidadas, changeToUppercase ()
y changeToLowercase ()
, tener acceso a los argumentos de la función externa, estados
, así como el nuevos estados
Variable declarada en la función exterior..
Dejame explicar que pasa.
los changeCase (mayúsculas: ofStrings :)
La función acepta un booleano como su primer argumento y un parámetro variadic de tipo Cuerda
como su segundo parámetro. La función devuelve una matriz de cadenas compuestas por las cadenas pasadas a la función como segundo argumento. En el cuerpo de la función, creamos una matriz mutable de cadenas, nuevosStrings
, en el que almacenamos las cadenas modificadas.
Las funciones anidadas recorren las cadenas que se pasan a changeCase (mayúsculas: ofStrings :)
Funciona y cambia el caso de cada cuerda. Como puede ver, tienen acceso directo a las cadenas que se pasan a la changeCase (mayúsculas: ofStrings :)
función, así como la nuevosStrings
matriz, que se declara en el cuerpo de la changeCase (mayúsculas: ofStrings :)
función.
Verificamos el valor de mayúscula
, Llame a la función apropiada, y devuelva el nuevosStrings
formación. Las dos líneas al final del ejemplo demuestran cómo el changeCase (mayúsculas: ofStrings :)
funciona la función.
Aunque he demostrado la captura de valor con funciones, recuerde que cada función es un cierre. En otras palabras, las mismas reglas se aplican a los cierres sin nombre.
Se ha mencionado varias veces en este artículo: las funciones son cierres. Hay tres tipos de cierres:
Funciones globales, como la imprimir (_: separador: terminador :)
La función de la biblioteca estándar de Swift, no captura valores. Sin embargo, las funciones anidadas tienen acceso y pueden capturar los valores de las constantes y los valores de la función en la que están definidas. El ejemplo anterior ilustra este concepto.
Las expresiones de cierre, también conocidas como cierres sin nombre, pueden capturar los valores de constantes y variables del contexto en el que se definen. Esto es muy similar a las funciones anidadas.
Un cierre que captura el valor de una variable puede cambiar el valor de esa variable. Swift es lo suficientemente inteligente como para saber si debe copiar o hacer referencia a los valores de las constantes y variables que captura.
Los desarrolladores que aprenden Swift y tienen poca experiencia con otros lenguajes de programación darán por sentado este comportamiento. Sin embargo, es una ventaja importante que Swift entienda cómo se utilizan los valores capturados en un cierre y, como resultado, puede manejar la administración de la memoria por nosotros.
Los cierres son un concepto importante, y los usarás a menudo en Swift. Le permiten escribir código dinámico y flexible que es fácil de escribir y de entender..
En el siguiente artículo, exploramos la programación orientada a objetos en Swift, comenzando con objetos, estructuras y clases.
Si desea aprender a usar Swift 3 para codificar funciones avanzadas para aplicaciones del mundo real, consulte nuestro curso Ir más lejos con Swift: animación, redes y controles personalizados. Sigue a Markus Mühlberger mientras codifica una aplicación meteorológica funcional de iOS con datos del clima en vivo, componentes personalizados de la interfaz de usuario y algunas animaciones ingeniosas para dar vida a todo.