Cierres de escape en Swift

Soy nuevo en Swift y estaba leyendo el manual cuando encontré cierres de escape. No recibí la descripción del manual en absoluto. ¿Podría alguien explicarme por favor qué son los cierres de fugas en Swift en términos simples?

Considera esta clase:

 class A { var closure: (() -> Void)? func someMethod(closure: () -> Void) { self.closure = closure } } 

someMethod asigna el cierre pasado, a una propiedad en la clase.

Ahora aquí viene otra clase:

 class B { var number = 0 var a: A = A() func anotherMethod() { a.someMethod { self.number = 10 } } } 

Si llamo a anotherMethod , el cierre { self.number = 10 } se almacenará en la instancia de A Como el self es capturado en el cierre, la instancia de A también lo hará referencia fuerte.

¡Eso es básicamente un ejemplo de un cierre fugado!

Probablemente te estés preguntando, “¿entonces? ¿De dónde escapó el cierre, y para?”

El cierre se escapa del scope del método, al scope de la clase. ¡Y puede ser llamado más tarde, incluso en otro hilo! Esto podría causar problemas si no se maneja adecuadamente.

Para evitar @noescape accidentales de cierres y provocar ciclos de retención y otros problemas, use el atributo @noescape :

 class A { var closure: (() -> Void)? func someMethod(@noescape closure: () -> Void) { } } 

Ahora bien, si intentas escribir self.closure = closure , ¡no comstack!

Actualizar:

En Swift 3, todos los parámetros de cierre no pueden escapar por defecto. Debe agregar el atributo @escaping para que el cierre pueda escapar del scope actual. ¡Esto agrega mucha más seguridad a su código!

 class A { var closure: (() -> Void)? func someMethod(closure: @escaping () -> Void) { } } 

Voy de una manera más simple.

Considera este ejemplo:

 func testFunctionWithNonescapingClosure(closure:() -> Void) { closure() } 

Lo anterior es un cierre no de escape porque se invoca el cierre antes de que regrese el método.

Considere el mismo ejemplo con una operación asincrónica:

 func testFunctionWithEscapingClosure(closure:@escaping () -> Void) { DispatchQueue.main.async { closure() } } 

El ejemplo anterior contiene un cierre de escape porque la invocación de cierre puede ocurrir después de que la función retorna debido a la operación asincrónica.

  var completionHandlers: [() -> Void] = [] func testFunctionWithEscapingClosure(closure: @escaping () -> Void) { completionHandlers.append(closure) } 

En el caso anterior, puede darse cuenta fácilmente de que el cierre se está moviendo fuera del cuerpo de la función, por lo que debe ser un cierre de escape.

El cierre de escape y sin escape se agregaron para la optimización del comstackdor en Swift 3. Puede buscar las ventajas del cierre de nonescaping .

Encuentro este sitio web muy útil en ese asunto. La explicación simple sería:

Si se pasa un cierre como argumento a una función y se invoca después de que la función retorna, el cierre está escapando.

¡Lea más en el enlace que pasé arriba! 🙂

Swift 4.1

From Language Reference: Atributos del lenguaje de progtwigción Swift (Swift 4.1)

Apple explica que el atributo escaping claramente.

Aplique este atributo al tipo de un parámetro en un método o statement de función para indicar que el valor del parámetro se puede almacenar para su posterior ejecución. Esto significa que se permite que el valor sobreviva a la duración de la llamada. Los parámetros de tipo de función con el atributo de tipo de escape requieren el uso explícito de uno mismo. para propiedades o métodos. Para ver un ejemplo de cómo usar el atributo de escape, vea Cierres de escape

 var completionHandlers: [() -> Void] = [] func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) { completionHandlers.append(completionHandler) } 

La función someFunctionWithEscapingClosure(_:) toma un cierre como argumento y lo agrega a una matriz que se declara fuera de la función. Si no marcó el parámetro de esta función con @escaping , obtendría un error en tiempo de comstackción.

Se dice que un cierre escapa a una función cuando el cierre se pasa como un argumento a la función, pero se invoca después de que la función retorna. Cuando declara una función que toma un cierre como uno de sus parámetros, puede escribir @escaping antes del tipo del parámetro para indicar que se permite que el cierre escape.