En Swift, las closures pueden "capturar" valores y referencias del contexto circundante. Si el valor capturado es un tipo por valor (struct, enum), la closure copia los datos. Si se captura una referencia (por ejemplo, una clase), la closure guarda una referencia a la instancia de la clase, lo que puede conducir a un ciclo de retención si no se utiliza [weak self] o [unowned self].
El mecanismo de captura permite implementar tareas asincrónicas y callbacks, sin embargo, un uso incorrecto dificulta la gestión de la memoria.
Ejemplo:
class MyClass { var value = 10 func doSomething() { let closure = { [weak self] in print(self?.value ?? "nil") } closure() } }
En este ejemplo, se utiliza [weak self] para evitar el ciclo de referencia fuerte, ya que se está realizando un trabajo asincrónico.
Pregunta: "¿Siempre garantiza el uso de [weak self] la ausencia de fugas de memoria al capturar self en una closure?"
Respuesta: No, si dentro de la closure aparece una referencia fuerte (por ejemplo, a través de variables intermedias), incluso con [weak self] se puede crear un ciclo de retención. Por ejemplo:
let closure = { [weak self] in let strongSelf = self strongSelf?.doSomething() }
Si se vincula la closure a un objeto de larga duración (por ejemplo, NotificationCenter), se puede producir un ciclo de retención si hay otras referencias a self.
Historia 1
En un proyecto de aplicación iOS, el desarrollador olvidó especificar
[weak self]al pasar la closure a una solicitud de red. Como resultado, mientras se ejecutaba la solicitud, el objeto del controlador de vista no se liberaba, incluso si el usuario cerraba la pantalla. Esto llevó a fugas de memoria durante el uso intensivo de operaciones de red.
Historia 2
En un sistema complejo de suscripción a un temporizador, el objeto que se suscribió a través de la closure sin usar
weakcontinuó realizando sus acciones incluso después de que la pantalla se eliminó de la memoria. Esto condujo a llamadas repetidas a elementos de la interfaz de usuario que ya no estaban en la jerarquía, causando caídas de la aplicación.
Historia 3
Al implementar caching de datos, la closure capturó una referencia a self sin la anotación
[unowned self]en un objeto de caché de larga duración. Después de la eliminación del objeto original, intentar acceder a self desde la closure provocaba un acceso a memoria ya liberada y causaba un derrape de la aplicación.