¿Por qué a los delegados de Objective-C generalmente se les da la propiedad asignar en lugar de retener?


Estoy navegando a través del maravilloso blog mantenido por Scott Stevenson, y estoy tratando de entender un Objetivo fundamental: el concepto C de asignar a los delegados la propiedad 'asignar' vs 'retener'. Tenga en cuenta que ambos son los mismos en un entorno de recolección de basura. Me preocupa sobre todo un entorno no basado en GC (por ejemplo: iPhone).

Directamente desde el blog de Scott:

" La palabra clave assign generará un setter que asigna el valor a la variable de instancia directamente, en lugar de copiarlo o retenerlo. Esto es mejor para tipos primitivos como NSInteger y CGFloat, u objetos que no posees directamente, como los delegados."

¿Qué significa que usted no posee directamente el objeto delegado? Normalmente retengo a mis delegados, porque si no quiero que se vayan al abismo, retain se encargará de eso por mí. Normalmente abstrajo UITableViewController de su fuente de datos respectiva y delego también. También conservo ese objeto en particular. Quiero hacer seguro que nunca se va así que mi UITableView siempre tiene su delegado alrededor.

¿Puede alguien explicar más dónde/por qué me equivoco, para que pueda entender este paradigma común en la programación de Objective-C 2.0 de usar la propiedad assign en los delegados en lugar de retain?

Gracias!

Author: Plumenator, 2009-05-28

4 answers

La razón por la que evita retener delegados es que necesita evitar un ciclo de retención:

A crea B A se establece como delegado de B … A es liberado por su propietario

Si B hubiera retenido A, A no sería liberado, ya que B posee A, por lo tanto, el dealloc de A nunca sería llamado, causando que tanto A como B se filtraran.

No debe preocuparse por A irse porque posee B y por lo tanto se deshace de él en dealloc.

 175
Author: Andrew Pouliot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-10 22:25:00

Porque el objeto que envía los mensajes del delegado no posee el delegado.

Muchas veces, es al revés, como cuando un controlador se establece como el delegado de una vista o ventana: el controlador posee la vista/ventana, por lo que si la vista/ventana posee su delegado, ambos objetos se poseerían entre sí. Esto, por supuesto, es un ciclo de retención, similar a una fuga con la misma consecuencia (los objetos que deberían estar muertos permanecen vivos).

Otras veces, los objetos son pares: ninguno de los dos posee el otro, probablemente porque ambos son propiedad del mismo tercer objeto.

De cualquier manera, el objeto con el delegado no debe retener a su delegado.

(Por cierto, hay al menos una excepción. No recuerdo qué era, y no creo que hubiera una buena razón para ello.)


Addendum (agregado 2012-05-19): En ARC, debe usar weak en lugar de assign. Las referencias débiles se establecen en nil automáticamente cuando el objeto muere, eliminando la posibilidad de que el objeto delegante termine enviando mensajes al delegado muerto.

Si se mantiene alejado de ARC por alguna razón, al menos cambie las propiedades assign que apuntan a objetos a unsafe_unretained, lo que hace explícito que se trata de una referencia sin retocar pero sin poner a cero a un objeto.

assign sigue siendo apropiado para valores no objeto tanto en ARC como en MRC.

 44
Author: Peter Hosey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-19 09:04:07

Tenga en cuenta que cuando tiene un delegado que se asigna, es muy importante establecer siempre ese valor de delegado en nil cuando el objeto va a ser desasignado, por lo que un objeto siempre debe tener cuidado de eliminar las referencias de delegado en dealloc si no lo ha hecho en otro lugar.

 17
Author: Kendall Helmstetter Gelner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-28 15:17:46

Una de las razones detrás de eso es evitar retener ciclos. Solo para evitar el escenario donde A y B ambos objetos se refieren entre sí y ninguno de ellos se libera de la memoria.

Acutally assign es mejor para tipos primitivos como NSInteger y CGFloat, u objetos que no posee directamente, como delegados.

 1
Author: Puneet Sharma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-28 10:01:51