El operador defer fue introducido en Swift para la limpieza segura y garantizada de recursos o la ejecución de código al salir del ámbito, de manera análoga a finally/using/RAII en otros lenguajes.
La inicialización en múltiples etapas, el trabajo con archivos, los escenarios cruzados de salida de una función requieren la liberación garantizada de recursos o la ejecución de lógica (cerrar archivo, desbloquear mutex, devolver objeto al pool, restablecer estado temporal). Antes de la aparición de defer, había que hacerlo manualmente en cada return.
defer garantiza la ejecución de su código al salir del scope actual, independientemente de si la salida es normal o a través de throw. Se pueden declarar múltiples defer — se ejecutarán en orden inverso al de la declaración.
Ejemplo de liberación de recurso:
func processFile(path: String) throws { let file = try openFile(path) defer { file.close() } // Se llamará garantizadamente incluso con errores // ... trabajo con file ... }
Características clave:
¿Puede defer capturar variables por referencia y cómo afecta esto el comportamiento de los closures?
Sí, defer captura todas las variables utilizadas en el momento de la llamada, según las reglas de captura de closure (copia para tipos de valor, referencia para tipos de referencia). Capturar una variable externa mutable será un error — puede cambiar en el momento de la ejecución del defer.
¿Cómo funciona un defer anidado dentro de ciclos y funciones?
Si un defer se declara dentro de un ciclo, se ejecuta al finalizar cada iteración del scope:
for _ in 1...3 { defer { print("Fin de la iteración") } print("Dentro") }
Imprimirá:
Dentro
Fin de la iteración
Dentro
Fin de la iteración
Dentro
Fin de la iteración
¿Puede defer no ejecutarse?
La ejecución del código defer está garantizada solo si realmente se sale del scope. Pero si el proceso termina inesperadamente (fatalError, crash), o se llama a exit, el defer no se ejecuta. Además, defer no funciona a nivel de métodos de objetos — solo en el scope de funciones/bloques.
En el código después de abrir un archivo y trabajar con él, se devolvió en diferentes return/throw, olvidaron cerrar el archivo en un lugar. Como resultado, el handle de archivo abierto se filtró al sistema.
Ventajas:
Desventajas:
Uso de defer para garantizar el desbloqueo de un mutex, la liberación de un descriptor de archivo y el restablecimiento de una bandera de progreso:
func criticalSection() { lock() defer { unlock() } // ... trabajo ... }
Ventajas:
Desventajas: