guide example basics apple ios swift automatic-ref-counting

ios - example - ¿Siempre usaremos[sin dueño] dentro de cierre en Swift?



swift reference (7)

Actualización 11/2016

Escribí un artículo sobre esto extendiendo esta respuesta (buscando en SIL para entender qué hace ARC), échale un vistazo here .

Respuesta original

Las respuestas anteriores no dan reglas directas sobre cuándo usar una sobre otra y por qué, así que permítanme agregar algunas cosas.

La discusión sin dueño o débil se reduce a una cuestión de vida útil de la variable y al cierre que la referencia.

Escenarios

Puedes tener dos escenarios posibles:

  1. El cierre tiene la misma duración de la variable, por lo que el cierre solo será accesible hasta que la variable sea accesible . La variable y el cierre tienen la misma vida útil. En este caso, debe declarar la referencia como sin dueño . Un ejemplo común es el [unowned self] utilizado en muchos ejemplos de cierres pequeños que hacen algo en el contexto de sus padres y que al no ser referenciado en ningún otro lugar no sobrevive a sus padres.

  2. El tiempo de vida del cierre es independiente del de la variable, el cierre aún podría ser referenciado cuando la variable ya no es alcanzable. En este caso, debe declarar la referencia como débil y verificar que no sea nula antes de usarla (no forzar el desenvolvimiento). Un ejemplo común de esto es el [weak delegate] que puede ver en algunos ejemplos de cierre que hace referencia a un objeto delegado completamente no relacionado (de por vida).

Uso actual

Entonces, ¿cuál / deberías usar la mayoría de las veces?

Citando a Joe Groff desde twitter :

Sin dueño es más rápido y permite inmutabilidad y no-opcionalidad.

Si no necesitas débiles, no lo uses.

here encontrará más información sobre los trabajos internos sin dueño * .

* Por lo general, también se hace referencia a no propiedad (seguro) para indicar que las verificaciones en tiempo de ejecución (que llevan a un bloqueo por referencias no válidas) se realizan antes de acceder a la referencia sin propiedad.

En la sesión 403 de WWDC 2014, Swift Intermedio y transcript , había la siguiente diapositiva

El orador dijo que en ese caso, si no usamos [unowned self] allí, habrá una pérdida de memoria. ¿Significa que siempre debemos usar [unowned self] dentro del cierre?

En la línea 64 de ViewController.swift de la aplicación Swift Weather , no uso [unowned self] . Pero actualizo la interfaz de usuario utilizando algunos @IBOutlet s como self.temperature y self.loadingIndicator . Puede estar bien porque todos los @IBOutlet s que he definido son weak . Pero por seguridad, ¿debemos usar siempre [unowned self] ?

class TempNotifier { var onChange: (Int) -> Void = {_ in } var currentTemp = 72 init() { onChange = { [unowned self] temp in self.currentTemp = temp } } }


Aquí hay citas brillantes de los foros de desarrolladores de Apple que se describen deliciosos detalles:

unowned vs unowned(safe) vs unowned(unsafe)

unowned(safe) es una referencia no propietaria que afirma en el acceso que el objeto aún está vivo. Es algo así como una referencia opcional débil que se desenvuelve implícitamente con x! cada vez que se accede. unowned(unsafe) es como __unsafe_unretained en ARC: es una referencia que no posee, pero no hay verificación de que el objeto aún esté vivo en el acceso, por lo que las referencias pendientes llegarán a la memoria basura. unowned es siempre un sinónimo de unowned(safe) actualmente, pero la intención es que se optimice para unowned(unsafe) -Ofast unowned(unsafe) en -Ofast compilaciones -Ofast cuando las verificaciones de tiempo de ejecución están deshabilitadas.

unowned vs weak

unowned realidad usa una implementación mucho más simple que weak . Los objetos Swift nativos llevan dos recuentos de referencias, y las referencias unowned propietario superan el recuento de referencias no poseídas en lugar del recuento de referencias fuertes . El objeto se desinicializa cuando su recuento de referencia fuerte llega a cero, pero en realidad no se desasigna hasta que el recuento de referencia sin propiedad también llega a cero. Esto hace que la memoria se mantenga un poco más larga cuando hay referencias sin propietario, pero eso no suele ser un problema cuando se usa unowned porque los objetos relacionados deberían tener vidas casi iguales de todos modos, y es mucho más simple y de menor costo que el Implementación basada en tablas laterales utilizada para poner a cero las referencias débiles.

Actualización: en el moderno Swift, weak utiliza internamente el mismo mecanismo que el no unowned . Así que esta comparación es incorrecta porque compara Objective-C weak con Swift sin unonwed .

Razones

¿Cuál es el propósito de mantener viva la memoria después de que las referencias sean 0? ¿Qué sucede si el código intenta hacer algo con el objeto utilizando una referencia sin dueño después de que se desinicializa?

La memoria se mantiene viva para que sus cuentas de retención aún estén disponibles. De esta manera, cuando alguien intenta retener una referencia fuerte al objeto sin dueño, el tiempo de ejecución puede verificar que el recuento de referencia fuerte sea mayor que cero para garantizar que sea seguro retener el objeto.

¿Qué sucede con las referencias de propiedad o sin propiedad del objeto? ¿Se desconecta su vida útil del objeto cuando se desinicializa o también se conserva su memoria hasta que el objeto se desasigna después de que se libera la última referencia no poseída?

Todos los recursos que posee el objeto se liberan tan pronto como se libera la última referencia segura del objeto y se ejecuta su deinit. Las referencias sin propiedad solo mantienen viva la memoria; además del encabezado con los recuentos de referencias, su contenido es basura.

Emocionado, ¿eh?


Hay algunas grandes respuestas aquí. Pero los cambios recientes en la forma en que Swift implementa las referencias débiles deberían cambiar el yo débil de todos frente a las decisiones de uso del auto sin dueño. Anteriormente, si necesitabas el mejor rendimiento, usar el yo sin dueño era superior al yo débil, siempre y cuando pudieras estar seguro de que el yo nunca sería nulo, porque acceder al yo sin dueño es mucho más rápido que acceder al yo débil.

Pero Mike Ash ha documentado cómo Swift ha actualizado la implementación de vars débiles para usar mesas auxiliares y cómo esto mejora sustancialmente el desempeño débil.

https://mikeash.com/pyblog/friday-qa-2017-09-22-swift-4-weak-references.html

Ahora que no hay una penalización de rendimiento significativa para un ser débil, creo que deberíamos usarlo de forma predeterminada en el futuro. El beneficio de ser débil es que es opcional, lo que hace que sea mucho más fácil escribir código más correcto, básicamente es la razón por la que Swift es un lenguaje tan bueno. Puede pensar que sabe qué situaciones son seguras para el uso de personas sin dueño, pero mi experiencia al revisar muchos otros códigos de desarrolladores es, la mayoría no. He arreglado muchos bloqueos en los que el auto sin propietario fue desasignado, generalmente en situaciones donde un hilo de fondo se completa después de que se desasigna un controlador.

Los errores y los bloqueos son las partes de programación más costosas, dolorosas y que consumen más tiempo. Haz tu mejor esfuerzo para escribir el código correcto y evitarlos. Recomiendo establecer una regla para no forzar nunca los opcionales de desenvolvimiento y nunca usar un yo sin dueño en lugar de un yo débil. No perderá nada que le falte las veces en que la fuerza se desenvuelve y el auto sin dueño en realidad es seguro. Pero ganará mucho eliminando fallos y errores difíciles de encontrar y depurar.


No, definitivamente hay momentos en los que no querrías usar [unowned self] . A veces, usted quiere que el cierre se capture a sí mismo para asegurarse de que todavía esté cerca para cuando se llame el cierre.

Ejemplo: realizar una solicitud de red asíncrona

Si está realizando una solicitud de red asíncrona , desea que el cierre se retenga para cuando finalice la solicitud. Ese objeto puede haber sido desasignado, pero todavía desea poder manejar la finalización de la solicitud.

Cuándo usar un unowned self o weak self

El único momento en el que realmente desea utilizar [unowned self] o [weak self] es cuando creará un ciclo de referencia fuerte . Un ciclo de referencia fuerte es cuando hay un bucle de propiedad en el que los objetos se poseen unos a otros (tal vez a través de un tercero) y, por lo tanto, nunca serán desasignados porque ambos se aseguran de que los demás se queden.

En el caso específico de un cierre, solo debe darse cuenta de que cualquier variable a la que se hace referencia dentro de él, es "propiedad" del cierre. Mientras el cierre esté alrededor, se garantiza que esos objetos estarán alrededor. La única forma de detener esa propiedad es hacer [unowned self] o [weak self] . Entonces, si una clase posee un cierre, y ese cierre captura una referencia fuerte a esa clase, entonces usted tiene un ciclo de referencia fuerte entre el cierre y la clase. Esto también incluye si la clase posee algo que posee el cierre.

Concretamente en el ejemplo del video.

En el ejemplo de la diapositiva, TempNotifier posee el cierre a través de la variable miembro onChange . Si no se declararan a self como unowned , el cierre también sería propio y crearía un fuerte ciclo de referencia.

Diferencia entre unowned y weak

La diferencia entre unowned y weak es que weak se declara como opcional, mientras que unowned no lo es. Al declararlo weak puedes manejar el caso de que podría ser nulo dentro del cierre en algún momento. Si intentas acceder a una variable unowned que resulta ser nula, bloqueará todo el programa. Por lo tanto, use solo unowned cuando esté seguro de que la variable siempre estará alrededor mientras el cierre esté alrededor.


Pensé que agregaría algunos ejemplos concretos específicamente para un controlador de vista. Muchas de las explicaciones, no solo aquí en , son realmente buenas, pero trabajo mejor con ejemplos del mundo real (@drewag tuvo un buen comienzo en esto):

  • Si tiene un cierre para manejar una respuesta de una red, las solicitudes son weak , porque son de larga duración. El controlador de vista podría cerrarse antes de que se complete la solicitud, por lo que self ya no apunta a un objeto válido cuando se llama al cierre.
  • Si tiene cierre que maneja un evento en un botón. Esto puede unowned tener unowned porque tan pronto como el controlador de la vista desaparece, el botón y cualquier otro elemento al que se hace referencia desde self desaparece al mismo tiempo. El bloque de cierre también desaparecerá al mismo tiempo.

    class MyViewController: UIViewController { @IBOutlet weak var myButton: UIButton! let networkManager = NetworkManager() let buttonPressClosure: () -> Void // closure must be held in this class. override func viewDidLoad() { // use unowned here buttonPressClosure = { [unowned self] in self.changeDisplayViewMode() // won''t happen after vc closes. } // use weak here networkManager.fetch(query: query) { [weak self] (results, error) in self?.updateUI() // could be called any time after vc closes } } @IBAction func buttonPress(self: Any) { buttonPressClosure() } // rest of class below. }


Según https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html

  • Las referencias débiles son siempre de un tipo opcional y se vuelven automáticamente nulas cuando se desasigna la instancia a la que hacen referencia.

  • Si la referencia capturada nunca llegará a ser nula, siempre debe capturarse como una referencia sin dueño, en lugar de una referencia débil

Ejemplo -

// if my response can nil use [weak self] resource.request().onComplete { [weak self] response in guard let strongSelf = self else { return } let model = strongSelf.updateModel(response) strongSelf.updateUI(model) } // Only use [unowned self] unowned if guarantees that response never nil resource.request().onComplete { [unowned self] response in let model = self.updateModel(response) self.updateUI(model) }