Historia de la pregunta:
El análisis de escape es un término de optimización de compiladores y gestión de memoria. En Swift, es importante debido al uso activo de closures y ARC. Se relaciona con el concepto de closures escapables y no escapables, que determinan si los closures pueden salir del ámbito de la función.
Problema:
La definición incorrecta del tipo de closure lleva a errores de propiedad de memoria, fugas, capturas inesperadas de variables y disminución del rendimiento. Es necesario entender claramente cuándo un closure "escapa", y cuándo no, y marcarlos correctamente.
Solución:
Swift requiere marcar explícitamente un closure escapable con el atributo @escaping. Los closures no escapables solo pueden ser capturados dentro de la función, tienen una gestión de memoria más eficiente y pueden usar las variables capturadas de forma más segura.
Ejemplo de la diferencia:
// closure no escapable (más rápido, no se retiene después de la llamada) func performSync(block: () -> Void) { block() } // closure escapable (puede ser almacenado y ejecutado más tarde) var storedCompletion: (() -> Void)? func performAsync(block: @escaping () -> Void) { storedCompletion = block }
Características clave:
¿Se puede cambiar un closure definido como no escapable a escapable sin modificar el código original de la función?
No. El atributo @escaping debe ser explícitamente indicado en la firma de la función. Si un closure dentro de la función se pasa fuera de ella, solo se necesita un closure escapable, de lo contrario, el compilador dará un error.
¿Es seguro pasar self dentro de un closure escapable sin capturas weak/unowned?
No. Un closure escapable puede potencialmente crear un retain cycle si captura self con una referencia fuerte. Es necesario gestionar explícitamente la captura:
someAsync { [weak self] in self?.doSomething() }
¿Siempre es un closure escapable global (se mantiene en memoria hasta el final del programa)?
No. Su ciclo de vida depende del ámbito de almacenamiento. Si un closure se guarda solo temporalmente o su propiedad se establece en nil, se liberará tan pronto como la propiedad pierda su dueño. Solo aquellos closures que contienen referencias a objetos globales o que los almacenan en variables globales se convierten en globales.
Dentro del método de una clase, se guarda un closure sin @escaping (error de compilador), luego se corrige, se olvidan sobre la protección contra el retain cycle — la aplicación tiene una fuga de memoria.
Ventajas:
Desventajas:
El desarrollador siempre verifica dónde se requiere un closure escapable, captura self como weak/unowned, se deshace de las fugas y trabaja con la memoria de forma segura.
Ventajas:
Desventajas: