¿Cuál es la diferencia entre una referencia débil y una referencia no propiedad?

240

Swift tiene:

  • Referencias fuertes
  • Referencias débiles
  • Referencias sin propietario

¿En qué se diferencia una referencia no propiedad de una referencia débil?

¿Cuándo es seguro usar una referencia no propiedad?

¿Son las referencias no propiedad un riesgo de seguridad como punteros colgantes en C / C ++?

Ian Ringrose
fuente
3
Muy buen artículo en andrewcbancroft.com/2015/05/08/…
Zeeshan
Mi experiencia es usar unownedpara las clases que controlamos, para las clases de Apple, usar weakporque no podemos garantizar con certeza lo que hace
onmyway133
@NoorAli, o "ownBy" como la referencia "sin propietario" a menudo apunta al propietario.
Ian Ringrose
1
NOTA: Hay importantes implicaciones de rendimiento a tener en cuenta con cada una de estas referencias: stackoverflow.com/questions/58635303/…
Epic Byte
@EpicByte A veces, un GC completo como Java o C # vale la pena.
Ian Ringrose

Respuestas:

361

Tanto las referencias weakcomo las unownedreferencias no crean una strongretención en el objeto referido (es decir, no aumentan el conteo 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 los Optionaltipos están integrados en el lenguaje Swift. En pocas palabras: los tipos opcionales ofrecen seguridad en la memoria (esto funciona muy bien con las reglas de construcción de Swift, que son estrictas para proporcionar este beneficio).

Una weakreferencia 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 lo obliga, tanto como puede, a escribir código seguro).

Una unownedreferencia supone que nunca se convertirá nildurante su vida útil. Se debe establecer una referencia no propia durante la inicialización; esto significa que la referencia se definirá como un tipo no opcional que se puede utilizar de forma segura sin controles. Si de alguna manera el objeto al que se hace referencia se desasigna, la aplicación se bloqueará cuando se use la referencia no propiedad.

De los documentos de Apple :

Utilice una referencia débil siempre que sea válida para que esa referencia sea nula en algún momento durante su vida útil. Por el contrario, use una referencia no propia 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 se extraen de los documentos .

Ejemplo de la weakpalabra 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 algunas ilustraciones ASCII (deberías ir a ver los documentos , tienen diagramas bonitos):

Person ===(strong)==> Apartment
Person <==(weak)===== Apartment

El ejemplo Persony Apartmentmuestra una situación en la que dos propiedades, que pueden 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 unownedpalabra 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 Customerpuede o no tener a CreditCard, pero a CreditCard siempre estará asociado con a Customer. Para representar esto, la Customerclase tiene una cardpropiedad opcional , pero la CreditCardclase tiene una propiedad no opcional (y no propia) customer.

Customer ===(strong)==> CreditCard
Customer <==(unowned)== CreditCard

El ejemplo Customery CreditCardmuestra una situación en la que una propiedad que puede ser nula y otra propiedad que no puede ser nula tiene el potencial de causar un ciclo de referencia fuerte. Este escenario se resuelve mejor con una referencia no propiedad.

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 una constante.

También hay un tercer escenario en el que ambas propiedades siempre deben tener un valor, y ninguna de ellas debe ser nula una vez que se complete la inicialización.

Y también están los escenarios clásicos de ciclo de retención para evitar al trabajar con cierres.

Para esto, te animo a que visites los documentos de Apple o leas el libro .

Ilea Cristian
fuente
3
Esto es algo trivial, pero encuentro el ejemplo del Apartamento y la Persona algo confuso, que también presenta una solución adicional para romper el ciclo de referencia fuerte. El apartamento de una persona es opcional y, por lo tanto, puede ser nulo, así como el inquilino de un apartamento es opcional y, por lo tanto, puede ser nulo, por lo que ambas propiedades pueden definirse como débiles. ``
Justin Levi Winter
clase Persona {let name: String init (name: String) {self.name = name} apartamento var débil: ¿Apartamento? } Apartamento de clase {let número: Int init (número: Int) {self.number = number} débil var inquilino: ¿Persona? }
Justin Levi Winter
3
¿Cuál es la diferencia entre el weak var Person?frente var Person??
Dean
44
@JustinLevi, si declara ambas propiedades como débiles, existe la posibilidad de que se desasignen. La Persona mantiene una fuerte referencia al Apartamento para que el Apartamento no sea desasignado. Si el departamento tuviera la misma referencia fuerte hacia la Persona, crearían un ciclo de retención, que el programador puede romper en tiempo de ejecución si lo sabe, pero de lo contrario es solo una pérdida de memoria. Esto es todo el alboroto sobre fuertes, débiles y sin dueño: administración de memoria en un nivel superior, porque ARC hace todo lo sucio por nosotros. Evitar retener ciclos es nuestro trabajo.
Ilea Cristian
1
¿El único beneficio de los no propietarios sobre los débiles es que no necesita desenvolverse y puede usar una constante? ¿Hay alguna instancia en la que no puedas usar débil y solo puedas usar sin dueño?
Alan
29

Q1. ¿En qué se diferencia una "referencia no propiedad" 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 se refiere y, por lo tanto, no impide que ARC elimine la instancia referenciada. Debido a que las referencias débiles pueden tener "ningún valor", debe declarar que cada referencia débil tiene un tipo opcional. (Documentos de Apple)

Referencia sin propietario:

Al igual que las referencias débiles, una referencia no propiedad no mantiene un fuerte control sobre la instancia a la que se refiere. Sin embargo, a diferencia de una referencia débil, se supone que una referencia no propiedad siempre tiene un valor. Debido a esto, una referencia no propiedad 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 sea nula en algún momento durante su vida útil. Por el contrario, use una referencia no propiedad cuando sepa que la referencia nunca será nula una vez que se haya establecido durante la inicialización. (Documentos de Apple)


Q2 ¿Cuándo es seguro usar una "referencia no propiedad"?

Como se citó anteriormente, se supone que una referencia no propiedad siempre tiene un valor. Por lo tanto, solo debe usarlo cuando esté seguro de que la referencia nunca será nula. Los documentos de Apple ilustran un caso de uso para referencias no propiedad a través del siguiente ejemplo.

Supongamos que tenemos dos clases Customery 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
}

Q3. ¿Son las referencias de "referencia no propiedad" un riesgo de seguridad como "punteros colgantes" en C / C ++

No lo creo.

Dado que las referencias no propiedad son solo referencias débiles que se garantiza que tienen un valor, no debería ser un riesgo de seguridad de ninguna manera. Sin embargo, si intenta acceder a una referencia no propiedad después de que la instancia a la que hace referencia se desasigna, activará un error de tiempo de ejecución y la aplicación se bloqueará.

Ese es el único riesgo que veo con él.

Enlace a documentos de Apple

Myxtic
fuente
su programa de Q2 ejemplo sencillo para entender sobre unowned..thanks .. ¿Se puede añadir mismo tipo de ejemplo para débil y fuerte ..
Ranjith Kumar
Excelente. Gracias.
Swifty McSwifterton
¿Puedes incluir un ejemplo común para personas sin dueño o débiles?
Miel
Considere los objetos padre e hijo, si el niño no puede existir sin un padre, úselo unownedpara la propiedad del padre en la clase hijo. débil es viceversa. Buena explicación @myxtic! unowned¡las referencias son solo weakreferencias que tienen un valor garantizado!
Saif
26

Si el self puede ser nulo en el cierre, use [self débil] .

Si el self nunca será nulo en el cierre, use [self sin dueño] .

Si se bloquea cuando usas [self sin dueño], entonces self probablemente sea nulo en algún momento de ese cierre y probablemente necesites usar [self débil] en su lugar.

Vea los ejemplos sobre el uso de cierres fuertes , débiles y sin dueño en cierres:

https://developer.apple.com/library/ios/documentation/swift/conceptual/swift_programming_language/AutomaticReferenceCounting.html

Tenaz
fuente
77
¿Por qué no usar solo débil incluso si uno mismo nunca puede ser nulo, no hay daño bien hecho?
Boon
44
hola @Boon: esa es de hecho la pregunta crítica.
Fattie
[self débil] => Si uso el cierre dentro de viewDidLoad (), ¿cómo puede selfser nulo?
Hassan Tareq
@HassanTareq, creo que se mencionan un par de buenos ejemplos en el artículo mencionado anteriormente. Consulte la sección "Resolución de ciclos de referencia fuertes para cierres", especialmente. Cita: "Swift requiere que escribas self.someProperty o self.someMethod () (en lugar de someProperty o someMethod ()) cada vez que te refieres a un miembro de self dentro de un cierre. Esto te ayuda a recordar que es posible capturarte a ti mismo accidente." Extracto de: Apple Inc. "El lenguaje de programación Swift (Swift 4)". iBooks. itunes.apple.com/de/book/the-swift-programming-language-swift-4/… "
Nick Entin
1
@Boon Si siempre usa débil, el compilador forzará a verificar si es opcional antes de usarlo. En caso de que no haya puesto esa verificación, dará un error de tiempo de compilación. No hay otro daño.
Vikas Mishra
5

Extractos del enlace

Pocos puntos finales

  • Para determinar si incluso necesita preocuparse por fuertes, débiles o sin dueño, pregunte: "¿Estoy tratando con tipos de referencia". Si está trabajando con estructuras o enumeraciones, ARC no está administrando la memoria para esos tipos y ni siquiera necesita preocuparse por especificar débiles o no propias para esas constantes o variables.
  • Las referencias fuertes están bien en las relaciones jerárquicas donde el padre hace referencia al hijo, pero no al revés. De hecho, las referencias fuertes son el tipo de referencia más apropiado la mayor parte del tiempo.
  • Cuando dos instancias están opcionalmente relacionadas entre sí, asegúrese de que una de esas instancias tenga una referencia débil a la otra.
  • Cuando dos instancias están relacionadas de tal manera que una de las instancias no puede existir sin la otra, la instancia con la dependencia obligatoria debe contener una referencia no propiedad de la otra instancia.
Abhinav Singh
fuente
1

Tanto las referencias weakcomo las unownedreferencias no afectarán el recuento de referencias del objeto. Pero la referencia débil siempre será opcional, es decir, puede ser nula, mientras que las unownedreferencias nunca pueden ser nulas, por lo que nunca serán opcionales. Cuando use una referencia opcional, siempre tendrá que manejar la posibilidad de que el objeto sea nulo. En caso de una referencia no propiedad, deberá asegurarse de que el objeto nunca sea nulo. Usar una referencia no propiedad de un objeto nulo será similar a desenvolver forzosamente un opcional que sea nulo.

Dicho esto, es seguro usar una referencia no propiedad donde esté seguro de que la vida útil del objeto es mayor que la referencia. Si ese no es el caso, es mejor usar una referencia débil en su lugar.

En cuanto a la tercera parte de la pregunta, no creo que la referencia no propiedad sea similar a un puntero colgante. Cuando hablamos del recuento de referencia, generalmente nos referimos al recuento de referencia fuerte del objeto. De manera similar, swift mantiene el recuento de referencias no deseadas y los recuentos de referencias débiles para el objeto (los puntos de referencia débiles a algo llamado "tabla lateral" en lugar del objeto mismo). Cuando el recuento de referencia fuerte llega a cero, el objeto se desinicializa, pero no se puede desasignar si el recuento de referencia no propiedad es mayor que cero.

Ahora, un puntero colgante es algo que apunta a una ubicación de memoria que ya ha sido desasignada. Pero con rapidez, dado que la memoria solo se puede desasignar mientras exista una referencia no deseada al objeto, no puede causar un puntero colgante.

Hay muchos artículos que analizan la administración rápida de memoria con más detalle. Aquí hay uno.

Deeksha Kaul
fuente
0

Las referencias no propiedad son un tipo de referencia débil utilizada en el caso de una relación del mismo tiempo de vida entre dos objetos, cuando un objeto solo debe ser propiedad de otro objeto. Es una forma de crear un enlace inmutable entre un objeto y una de sus propiedades.

En el ejemplo dado en el video intermedio rápido de WWDC, una persona posee una tarjeta de crédito, y una tarjeta de crédito solo puede tener un titular. En la tarjeta de crédito, la persona no debe ser una propiedad opcional, ya que no desea tener una tarjeta de crédito flotando con un solo propietario. Puede romper este ciclo haciendo que la propiedad del titular del crédito sea una referencia débil, pero eso también requiere que lo haga opcional y variable (en lugar de constante). La referencia no propiedad en este caso significa que aunque CreditCard no tiene una participación en una Persona, su vida depende de ello.

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

    init (holder: Person) {
        self.holder = holder
    }
}
JuJoDi
fuente
enlace al video o título de wwdc?
Osa
-2

Úselo unownedcuando esté seguro de selfque nunca puede estar nilen el punto de acceso selfen ese punto.

Ejemplo (por supuesto, puede agregar el objetivo directamente MyViewController, pero de nuevo, es un ejemplo simple):

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let myButton = MyButton { [unowned self] in
            print("At this point, self can NEVER be nil. You are safe to use unowned.")
            print("This is because myButton can not be referenced without/outside this instance (myViewController)")
        }
    }
}

class MyButton: UIButton {
    var clicked: (() -> ())

    init(clicked: (() -> ())) {
        self.clicked = clicked

        // We use constraints to layout the view. We don't explicitly set the frame.
        super.init(frame: .zero)

        addTarget(self, action: #selector(clicked), for: .touchUpInside)
    }

    @objc private func sendClosure() {
        clicked()
    }
}

Úselo weakcuando existe la posibilidad de que selfpueda estar nilen el punto de acceso self.

Ejemplo:

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        NetworkManager.sharedInstance.receivedData = { [weak self] (data) in
            print("Can you guarentee that self is always available when the network manager received data?")
            print("Nope, you can't. Network manager will be alive, regardless of this particular instance of MyViewController")
            print("You should use weak self here, since you are not sure if this instance is still alive for every")
            print("future callback of network manager")
        }
    }
}

class NetworkManager {

    static let sharedInstance = NetworkManager()

    var receivedData: ((Data) -> ())?

    private func process(_ data: Data) {
        // process the data...

        // ... eventually notify a possible listener.
        receivedData?(data)
    }
}

Contras de unowned:

  • Más eficiente que débil
  • Puede (bueno, está obligado) a marcar la instancia como inmutable (ya no desde Swift 5.0).
  • Indica al lector de su código: Esta instancia tiene una relación con X y no puede vivir sin ella, pero si X se va, yo también me voy.

Contras de weak:

  • Más seguro que sin dueño (ya que no puede bloquearse).
  • Puede crear una relación con X que va en ambos sentidos, pero ambos pueden vivir el uno sin el otro.

Si no estás seguro, úsalo weak. ¡Espere , quiero decir, pregunte aquí en StackOverflow qué debe hacer en su caso! Usar débil todo el tiempo cuando no deberías es solo confuso para ti y para el lector de tu código.

J. Doe
fuente