2009-05-28 14 views
174

Estoy navegando a través del maravilloso blog mantenido por Scott Stevenson, y estoy tratando de comprender un concepto fundamental de Objective-C para asignar a los delegados la propiedad 'asignar' frente a 'retener'. Tenga en cuenta que ambos son iguales en un entorno recolectado de basura. Me preocupa sobre todo un entorno no basado en GC (p. Ej .: iPhone).¿Por qué a los delegados de Objective-C generalmente se les asigna la propiedad en lugar de retener?

Directamente desde el blog de Scott:.

"La palabra clave de asignación generará un colocador que asigna el valor de la variable de instancia directamente, en lugar de copiar o retenerlo Este es el mejor para los tipos primitivos como NSInteger y CGFloat, o objetos que no posee directamente, como 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, retener se encargará de eso por mí. Normalmente, elimino UITableViewController de su respectivo dataSource y también lo delego. También retengo ese objeto en particular. Quiero asegurarme de que nunca desaparezca, por lo que mi UITableView siempre tiene su delegado.

¿Alguien más puede explicar dónde/por qué estoy equivocado, entonces puedo 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 retener?

Gracias!

+0

Reetiquetado con "delegados" y sin "iphone". –

+0

¿Por qué el delegado asigna en lugar de copiar (como NSString?) – OMGPOP

Respuesta

174

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

A crea B A presenta a sí misma como el delegado del B ... A es liberado por su dueño

Si B había retenido A, A no se liberaría, ya que B posee A, por lo tanto, nunca se llamaría al dealloc de A, causando que A y B fugas.

No debe preocuparse de que A se vaya porque posee B y así se deshace de él en dealloc.

+0

No estoy de acuerdo, Mike. Acabo de encontrar un problema donde un modal tiene un delegado que descarta el modal. Pero cuando hago una advertencia de memoria en el modal, libera al delegado. Luego, cuando voy a descartar mi modal, el delegado es nulo. Choque. –

+0

Ok, no estoy de acuerdo, pero tiene razón en que es un defecto de diseño. Descubrí que estaba transmitiendo mi llamada de desestimación a través de una clase para niños del verdadero expulsor. Esa clase infantil se liberó y no pudo pasar al delegado contenedor del modal para descartar. Lo cambié para pasarle un puntero al delegado final y ese no fue liberado en la advertencia de memoria y todo está bien. –

+2

Su código nunca debe escribirse de tal manera que un delegado nulo cause su bloqueo. Solo el objeto propietario debe tener una referencia propietaria. Cuando se destraba, debe establecer nulos los delegados de los objetos propios antes de liberarlos. Entonces, cualquier mensaje enviado al delegado nulo simplemente se ignora. Sin embargo, al pasar un objeto nil * en *, un mensaje puede bloquearse. Solo asegúrate de no tratar con los delegados de esa manera. –

44

Porque el objeto que envía los mensajes de 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, así que si la vista/ventana poseía su delegado, ambos objetos serían poseyéndonos el uno al otro. 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 posee el otro, probablemente porque ambos son propiedad del mismo tercer objeto.

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

(Hay al menos una excepción, por cierto. No me acuerdo de lo que era, y no creo que había una buena razón para ello.)


Adición (agregado 2012-05-19): en ARC, debe usar weak en lugar de assign. Las referencias débiles se configuran en nil automáticamente cuando el objeto muere, lo que elimina la posibilidad de que el objeto delegado termine enviando mensajes al delegado muerto.

Si se está alejando de ARC por alguna razón, al menos cambie assign propiedades que apuntan a objetos a unsafe_unretained, que hacen explícito que esta es una referencia no retenida pero no cero a un objeto.

assign sigue siendo apropiado para los valores que no son objetos tanto en ARC como en MRC.

+13

'NSURLConnection' conserva su delegado. –

+0

Sí, use 'weak', pero eso no responde a la pregunta original: ¿Por qué Apple usa' assign' en lugar de 'weak'? – wcochran

+0

@wcochran: La pregunta original era "¿por qué las propiedades delegadas reciben' assign' en lugar de * retener * "; 'weak' no existía cuando se le preguntó. Tu pregunta es diferente, y debes preguntarla por separado. Estaré encantado de responderlo. –

17

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

+0

"Tenga en cuenta que cuando tiene un delegado asignado, es muy importante establecer siempre ese valor de delegado en cero siempre que el objeto va a ser desasignado" ¿Por qué? –

+2

Debido a que cualquier referencia dejada establecida, será inválida después de que un objeto sea desasignado (apuntando a la memoria que ya no está asignada al tipo de objeto esperado), y causará un bloqueo si intenta usarlo. Un signo de esto en el depurador es cuando el depurador afirma que alguna variable tiene un tipo que parece totalmente incorrecto de lo que la variable realmente se declara. –

+1

Esto solo es necesario si el objeto del que es delegado está siendo retenido por otra fuente, como un temporizador u otra devolución de llamada asincrónica. De lo contrario, se desmantelará después de que lo libere y no intentará llamar a los métodos de delegado. –

1

Una de las razones detrás de eso es evitar ciclos de retención. Solo para evitar el escenario donde A y B ambos objetos se referencian 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 son de su propiedad, como los delegados.

+0

Eso está más bien copiado de la cita del OP y la respuesta aceptada respectivamente, ¿no es así? – dakab

Cuestiones relacionadas