Tanto weak como unowned existen para evitar ciclos de retención como se ilustra en el siguiente ejemplo:
class A {
var b: B?
}
class B {
var a: A?
}
var pa: A? = A()
var pb: B? = B()
pb.a = pa
pa.b = pb
pa = nil
pb = nil
weak
- No aumenta el conteo de referencias.
- Siempre es opcional (
?) - Cuando el objeto apuntado se libera, la referencia se pone en
nilautomáticamente. - Se usa en el patrón delegado, en "capture list"s, y en relaciones entre dos instancias donde una puede desaparecer antes.
protocol SomeDelegate: AnyObject { }
class SomeClass {
weak var delegate: SomeDelegate?
}
unowned
- No incrementa el conteo de referencias.
- NO ES OPCIONAL
- Asume que el objeto siempre va a existir mientras se use.
- La aplicación se explota si accedo al objeto después de que fue liberado.
- Se usa cuando las dos instancias tienen la misma vida útil o cuando una nunca puede vivir sin la otra. Esta relación lógica es obligatoria.
En el siguiente ejemplo, si Mom se libera, Baby no debería existir.
class Mom {
let baby: Baby
init() {
baby = Baby(mom: self)
}
}
class Baby {
unowned let mom: Mom
init(mom: Mom) {
self.mom = mom
}
}
Diferencia en memoria entre weak y unowned
Todos los objetos almacenan en memoria (entre otras cosas):
- Los datos propios
- El conteo de referencias fuertes.
- Una tabla de referencias débiles (si existen)
- Metadata de ARC.
¿Qué pasa internamente con weak?
Cuando se marca una referencia a un objeto x como weak, el runtime de iOS registra esa referencia en la tabla de referencias débiles del objeto apuntado (i.e. x), que no afecta el conteo de referencias fuertes.
Luego, cuando el contador de referencias fuertes de x llega a cero:
- en runtime del sistema recorre la tabla de referencias débiles del objeto y las pone todas en
nil. - ejecuta el
deinitdex - Se libera la memoria del objeto.
Es de señalar que en el proceso antes mencionado no queda ningún puntero colgando ("dangling pointer").
¿Qué pasa internamente con unowned?
Al usar unowned no se registra ninguna referencia en la tabla de referencias débiles. Solo es un puntero sin retención, algo así como un "raw pointer" de C++. No se incrementa el conteo de referencias fuertes, así que no hay forma de que el runtime lo rastree.
Cuando el contador de referencias del objeto x llega a cero, ARC libera la instancia, ejecutando deinit y liberando la memoria. No hay manera de advertir a las dependencias acerca de esto que ocurrió, por lo que podemos decir que los punterosunowned` quedaron colgando ("dangling pointer").
Luego, si se trata de acceder a la instancia se va a tener EXC_BAD_ACCESS. No hay manera de que el runtime nos proteja porque no tiene ni idea de la existencia de esa referencia.
unowned no se puede volver nil porque no hay mecanismo de notificación.
Top comments (0)