2010-04-21 17 views
17

A tiene un controlador de vista y crea un objeto "descargador", que tiene una referencia al controlador de vista (como un delegado). El descargador devuelve la llamada al controlador de vista si descarga el elemento correctamente. Esto funciona bien siempre y cuando permanezca en la vista, pero si se va antes de que la descarga se complete, obtengo EXC_BAD_ACCESS. Entiendo por qué sucede esto, pero ¿hay alguna forma de verificar si un objeto todavía está asignado? Traté de probar usando delegate != nil y [delegate respondsToSelector:], pero ahoga.Evitar EXC_BAD_ACCESS al usar el patrón de delegado

if (!self.delegate || ![self.delegate respondsToSelector:@selector(downloadComplete:)]) { 
    // delegate is gone, go away quietly 
     [self autorelease]; 
     return; 
    } 
else { 
    // delegate is still around 
    [self.delegate downloadComplete:result]; 
} 

sé que podría,

a) han retener los objetos descargador el controlador de vista

b) mantener un conjunto de descargadores en el controlador de vista, y establecer sus valores de delegado a cero cuando Desbloqueo el controlador de vista.

Pero me pregunto si hay una manera más fácil, donde solo pruebo si la dirección del delegado contiene un objeto válido.

+0

Si pudiera probar si una dirección contenía un objeto válido, por definición, lo haría, porque sería válido acceder a ella para probarla? –

+1

objetivo-c tiene muchas abstracciones ... Puedo imaginarme un mundo donde el tiempo de ejecución fue capaz de distinguir entre una dirección con un objeto válido y una desasignada. –

Respuesta

10

No, no puede (de manera útil) "comprobar si una dirección contiene un objeto válido". Incluso si pudieras hurgar dentro de las partes internas del sistema de asignación de memoria y determinar que tu dirección apunta a un objeto válido, eso no significa necesariamente que era el mismo objeto al que te referías anteriormente: el objeto podría han sido desasignados y se ha creado otro objeto en la misma dirección de memoria.

Retener al delegado es la forma habitual de resolver esto. Su opción (b) rompe la encapsulación de objetos y puede tener problemas de seguridad de subprocesos.

+0

la mayoría de las implementaciones de apple que puedo ver no retienen al delegado ... p. '@property (no atómico, asignar) id delegado;' ¿Estoy aplicando incorrectamente el patrón de diseño? –

+0

Esto puede ser cierto para los objetos UI que requieren toda interacción en el hilo principal, pero creo que encontrará que las implementaciones que realizan operaciones asincrónicas en subprocesos de fondo (como NSURLConnection) retendrán su delegado. –

+6

¡Un ** delegado ** NUNCA se retiene! Solo ** objetivos ** se conservan. –

1

yo sólo escribo

SEL slc = @selector(theSlc); 
if ([delegate respondsToSelector:slc]) { 
    [delegate performSelector:slc]; 
} 

Si el objeto es válido el método será llamado, de lo contrario no. Usted no tiene que comprobar si hay

self.delegate != nil 
+4

Probablemente tengas razón de que mi '! Self.delegate' es redundante. Pero, sí sé que '[delegate respondsToSelector:]' no lo protege de 'EXC_BAD_ACCESS'. Intente ejecutar esto: \t NSObject * obj = [[NSObject alloc] init]; \t [obj release]; \t para (int i = 0; i <2; i ++) { \t \t if ([obj respondsToSelector: @selector (retainCount)]) NSLog (@ "conservar el recuento:% i", [obj retainCount]); \t} –

1

me encontré con esta pregunta porque mi objeto "descargador" me estaba dando EXC_BAD_ACCESS. Mi solución fue cancelar el objeto de descarga justo antes de lanzarlo. Suponiendo que usa NSURLConnection en su objeto de descarga, llame al método de cancelación en él.

Es importante tener en cuenta que si NSURLConnection no está descargando nada en ese momento, cancelar la llamada provocará un bloqueo. Necesitará cierta lógica para verificar si una descarga está en progreso.

1

También tengo problemas con la referencia débil del delegado, y actualmente tengo solo una solución para este problema: use la referencia fuerte para el delegado, y configure manualmente self.delegate = nil; después de que mi código esté completo. Esta solución funciona para mí en la carga de imágenes asíncronas, donde tienes algún ciclo de vida con final visible.

27

Acabo de encontrarme con este problema y lo solucioné. Para ARC, la solución es usar el atributo weak en lugar de assign.

El accidente venir porque el delegado

  1. tiene un atributo assign, Y
  2. Se ha desasignado.

La solución es utilizar el atributo weak, porque cuando los desasigna objeto, la voluntad de puntero pueden establecer la nil. Por lo tanto, cuando su código llame al respondsToSelector en un nil, el Objetivo C ignorará la llamada y no se bloqueará.

En su código, cuando intenta llamar al método respondsToSelector en delegate, obtiene un EXC_BAD_ACCESS. Esto se debe a que los objetos que usan la propiedad assign no se establecerán en nil cuando se desasignan. (De ahí por qué hacer un !self.delegate antes de la respondsToSelector no impide que la responseToSelector de ser llamado en un objeto desasignado, y todavía se bloquea su código)

Como ya se ha mencionado, el uso de un atributo strong o assign de un delegado (como muchos han mencionado) en ARC dará como resultado un ciclo de retención. Entonces no lo hagas, no es necesario.

+0

además de usar débil, ¿se necesitan otras cosas? Como por ejemplo, 'delegate = nil' en la clase principal o configuración 'id _strong delegate'? Supongo que estoy teniendo el mismo problema http://stackoverflow.com/questions/24466244/delegate-dont-exist-exc-bad-access/24970685#24970685 – Hexark

+2

No debería tener que hacer nada más. Leí su pregunta y noté que proporcionó una respuesta indicando que al usar 'weak' en lugar de' assign' se corrigió el bloqueo. ¿Sigues teniendo problemas? –

+0

Parece que el problema se ha resuelto :) – Hexark

Cuestiones relacionadas