ProgramaciónDesarrollador iOS

¿Qué son los closures escaping y non-escaping en Swift? ¿Cómo organizar correctamente el trabajo con closures, qué matices existen en su uso y qué problemas pueden surgir al trabajar con código asíncrono?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Closure escaping es un closure que "sale" del ámbito de la función que lo llama (por ejemplo, se guarda para su ejecución asíncrona).

Por defecto, en Swift, las funciones aceptan closures non-escaping: el closure se ejecuta dentro de la llamada a la función.

Para indicar explícitamente que es escaping se utiliza la palabra clave @escaping:

func asyncTask(completion: @escaping () -> Void) { DispatchQueue.main.async { completion() } }

Diferencias clave:

  • Los closures escaping pueden almacenarse y ser llamados más tarde.
  • Para capturar self en un closure escaping, es necesario indicar explícitamente [weak self] o [unowned self] para evitar pérdidas de memoria (retain cycle).

Pregunta capciosa.

¿Es necesario siempre escribir @escaping para closures pasados como parámetros a la función que llama a DispatchQueue.async?

— Sí. Dado que DispatchQueue.async guarda el closure hasta el momento de su ejecución, el closure se convierte en escaping.

Ejemplo:

func foo(action: () -> Void) { DispatchQueue.main.async { action() // No se compilará: el closure debe ser escaping. } } // Necesario: func bar(action: @escaping () -> Void) { ... }

Ejemplos de errores reales debido a la falta de conocimiento de los matices del tema.


Historia

El controlador creó una referencia fuerte a self dentro de un closure escaping (por ejemplo, una solicitud de red). El controlador no se liberó después de salir de la pantalla — un ciclo de retención fuerte. Solución: usar [weak self] o [unowned self].


Historia

La función pasaba un closure a DispatchQueue.async, pero no lo marcó como escaping. El proyecto no se compilaba, y los errores eran difíciles de encontrar debido a la anidación de funciones.


Historia

Dentro del closure se hacía referencia a un objeto que ya había sido de-inicializado al momento de la llamada del closure (se usó [unowned self]). Como resultado: crash en tiempo de ejecución. Solución: usar [weak self] y verificar si es nil.