¿Siempre es necesario usar weak o unowned en la lista de captura?
sobes.tech AI
Respuesta de la IA
No, no siempre.
Utilizamos weak o unowned en la lista de captura para prevenir ciclos de retención.
Un ciclo de retención ocurre cuando dos o más objetos se refieren entre sí de manera fuerte, impidiendo que la memoria sea liberada.
Se debe usar weak o unowned cuando un cierre hace referencia fuerte a self (u otra variable de instancia de clase/actor), y a su vez self (u otro objeto) tiene una referencia fuerte a ese cierre.
Ejemplos de situaciones donde esto es necesario:
- Operaciones asíncronas (solicitudes de red, procesamiento de datos)
- Patrón Observer
- Bloques de animación
- Colas de despacho
Si el cierre no captura self (u otra variable de clase/actor que tenga una referencia fuerte al cierre), o captura tipos estructurales/enumeraciones, no es necesario usar weak o unowned.
Diferencias entre weak y unowned:
| Tipo | Descripción | Cuándo usar |
|---|---|---|
weak |
Referencia débil (opcional). Puede ser nil. |
Cuando el ciclo de vida del cierre es más corto o igual al ciclo de vida del objeto capturado, y el objeto puede volverse nil antes de que termine el cierre. |
unowned |
Referencia no opcional. No puede ser nil. |
Cuando el ciclo de vida del cierre es más corto o igual al ciclo de vida del objeto capturado, y garantiza que el objeto existirá hasta que termine el cierre. Si el objeto se libera antes de que se ejecute el cierre, ocurrirá un error (referencia unowned a una instancia liberada). |
Ejemplo de uso de weak en la lista de captura:
class MyClass {
var name: String = "Test"
func performAsyncOperation() {
// El cierre captura `self`.
// MyClass puede ser liberado antes de que termine la operación asíncrona.
DispatchQueue.global().async { [weak self] in
guard let self = self else {
// `self` fue liberado, salir
return
}
print("Operación completada para \(self.name)")
}
}
deinit {
print("MyClass liberado")
}
}
Ejemplo de uso de unowned en la lista de captura:
class ViewController {
var dismissButton: UIButton!
func setupButton() {
dismissButton.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
// El cierre captura `self`.
// Suponemos que `self` (ViewController) vive más o igual que la acción del botón.
// En este caso, el target-acción no es un cierre, pero ilustra la idea de `unowned` en ciclos de referencia fuertes.
// Si esto *fuera* un cierre, por ejemplo, en un manejador de evento UIControl.Event,
// y ViewController existe durante el clic:
// dismissButton.addAction(UIAction { [unowned self] _ in
// self.dismiss(animated: true, completion: nil)
// }, for: .touchUpInside)
}
@objc func dismiss() {
// Cerrar el controlador de vista
}
}
Si el cierre no crea un ciclo de retención, no es obligatorio usar weak o unowned, e incluso puede causar verificaciones innecesarias (en caso de weak) o errores potenciales (en caso de unowned).
Por ejemplo, un cierre que no captura self u otro objeto, que tiene una referencia fuerte en el cierre:
func fetchData(completion: @escaping (Data) -> Void) {
URLSession.shared.dataTask(with: URL(string: "...")!) { data, response, error in
guard let data = data else { return }
// Aquí no hay captura de `self` u otros objetos que puedan causar un ciclo de retención.
completion(data)
}.resume()
}
En este caso, no se requiere [weak self] ni [unowned self] en la lista de captura.