¿Cuál es la diferencia entre una referencia débil y una referencia sin propietario?
Swift tiene:
- Referencias fuertes
- Referencias débiles
- Referencias sin dueño
¿En qué se diferencia una referencia sin propietario de una referencia débil?
¿Cuándo es seguro utilizar una referencia sin propietario?
¿Las referencias sin propietario son un riesgo para la seguridad, como los punteros colgantes en C/C++?
Ambas referencias weak
y unowned
no crean una strong
retención en el objeto referido (es decir, no aumentan el recuento de retención para evitar que ARC desasigne el objeto referido).
Pero ¿por qué dos palabras clave? Esta distinción tiene que ver con el hecho de que Optional
los tipos están integrados en el lenguaje Swift. Para resumir, los tipos opcionales ofrecen seguridad de memoria (esto funciona muy bien con las reglas del constructor de Swift , que son estrictas para brindar este beneficio).
Una weak
referencia permite la posibilidad de que se convierta nil
(esto sucede automáticamente cuando el objeto referenciado se desasigna), por lo tanto, el tipo de su propiedad debe ser opcional, por lo que usted, como programador, está obligado a verificarlo antes de usarlo (básicamente el El compilador te obliga, en la medida de lo posible, a escribir código seguro).
Una unowned
referencia supone que nunca lo será nil
durante su vida. Se debe establecer una referencia sin propietario durante la inicialización; esto significa que la referencia se definirá como un tipo no opcional que se puede utilizar de forma segura sin comprobaciones. Si de alguna manera se desasigna el objeto al que se hace referencia, la aplicación se bloqueará cuando se utilice la referencia sin propietario.
De los documentos de Apple :
Utilice una referencia débil siempre que sea válida para que esa referencia se vuelva nula en algún momento durante su vida. Por el contrario, utilice una referencia sin propietario cuando sepa que la referencia nunca será nula una vez que se haya establecido durante la inicialización.
En los documentos, hay algunos ejemplos que analizan los ciclos de retención y cómo romperlos. Todos estos ejemplos están extraídos de los documentos .
Ejemplo de la weak
palabra clave:
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
}
class Apartment {
let number: Int
init(number: Int) { self.number = number }
weak var tenant: Person?
}
Y ahora, para ver algo de arte ASCII (deberías ver los documentos , tienen diagramas bonitos):
Person ===(strong)==> Apartment
Person <==(weak)===== Apartment
El ejemplo Person
y Apartment
muestra una situación en la que dos propiedades, a las cuales se les permite ser nulas, tienen el potencial de causar un ciclo de referencia fuerte. Este escenario se resuelve mejor con una referencia débil. Ambas entidades pueden existir sin tener una dependencia estricta de la otra.
Ejemplo de la unowned
palabra clave:
class Customer {
let name: String
var card: CreditCard?
init(name: String) { self.name = name }
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) { self.number = number; self.customer = customer }
}
En este ejemplo, a Customer
puede tener o no un CreditCard
, pero a CreditCard
siempre estará asociado con un Customer
. Para representar esto, la clase tiene una propiedad Customer
opcional , pero la clase tiene una propiedad no opcional (y sin propietario) .card
CreditCard
customer
Customer ===(strong)==> CreditCard
Customer <==(unowned)== CreditCard
El ejemplo Customer
y CreditCard
muestra una situación en la que una propiedad a la que se le permite ser nula y otra propiedad que no puede ser nula tiene el potencial de causar un fuerte ciclo de referencia. Este escenario se resuelve mejor con una referencia sin propietario.
Nota de Apple:
Las referencias débiles deben declararse como variables para indicar que su valor puede cambiar en tiempo de ejecución. Una referencia débil no puede declararse como constante.
También existe un tercer escenario en el que ambas propiedades siempre deben tener un valor y ninguna de ellas debe ser nula una vez completada la inicialización.
Y también existen los escenarios clásicos del ciclo de retención que se deben evitar cuando se trabaja con cierres.
Para ello, te animo a que visites los documentos de Apple o leas el libro .
P1. ¿En qué se diferencia una “referencia sin propietario” de una “referencia débil”?
Referencia débil:
Una referencia débil es una referencia que no mantiene un fuerte control sobre la instancia a la que hace referencia y, por lo tanto, no impide que ARC elimine la instancia a la que se hace referencia. Debido a que se permite que las referencias débiles "no tengan valor", debe declarar que cada referencia débil tiene un tipo opcional. (Documentos de Apple)
Referencia sin dueño:
Al igual que las referencias débiles, una referencia sin propietario no mantiene un control firme sobre la instancia a la que hace referencia. Sin embargo, a diferencia de una referencia débil, se supone que una referencia sin propietario siempre tiene un valor. Debido a esto, una referencia sin propietario siempre se define como un tipo no opcional. (Documentos de Apple)
Cuándo usar cada uno:
Utilice una referencia débil siempre que sea válida para que esa referencia se vuelva nula en algún momento durante su vida. Por el contrario, utilice una referencia sin propietario cuando sepa que la referencia nunca será nula una vez que se haya establecido durante la inicialización. (Documentos de Apple)
P2. ¿Cuándo es seguro utilizar una “referencia sin propietario”?
Como se mencionó anteriormente, se supone que una referencia sin propietario siempre tiene un valor. Por lo que sólo debes usarlo cuando estés seguro de que la referencia nunca será nula. Apple Docs ilustra un caso de uso para referencias sin propietario mediante el siguiente ejemplo.
Supongamos que tenemos dos clases Customer
y CreditCard
. Un cliente puede existir sin una tarjeta de crédito, pero una tarjeta de crédito no existirá sin un cliente, es decir, se puede suponer que una tarjeta de crédito siempre tendrá un cliente. Entonces, deberían tener la siguiente relación:
class Customer {
var card: CreditCard?
}
class CreditCard {
unowned let customer: Customer
}
P3. ¿La referencia de "referencia sin propietario" es un riesgo de seguridad como los "punteros colgantes" en C/C++?
No me parece.
Dado que las referencias sin propietario son solo referencias débiles cuyo valor está garantizado, no debería representar un riesgo para la seguridad de ninguna manera. Sin embargo, si intenta acceder a una referencia sin propietario después de que se cancele la asignación de la instancia a la que hace referencia, se generará un error de tiempo de ejecución y la aplicación se bloqueará.
Ese es el único riesgo que le veo.
Enlace a documentos de Apple