2010-04-21 15 views
17

A avere un controller di visualizzazione e crea un oggetto "downloader", che ha un riferimento al controller di visualizzazione (come delegato). Il downloader richiama il controller della vista se scarica correttamente l'elemento. Funziona bene finché rimani sulla visuale, ma se ti allontani prima che il download sia completo ricevo EXC_BAD_ACCESS. Capisco perché questo sta accadendo, ma c'è un modo per verificare se un oggetto è ancora assegnato? Ho provato a provare usando delegate != nil e [delegate respondsToSelector:], ma soffoca.Evitare EXC_BAD_ACCESS quando si utilizza il pattern delegato

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]; 
} 

So che potrebbe,

a) sono gli oggetti downloader trattenere il controller della vista

b) mantenere un array di downloaders nel controller della vista, e impostare i valori delegato a zero quando Ho deallocato il controller di visualizzazione.

Ma mi chiedo se esiste un modo più semplice, in cui ho appena verificato se l'indirizzo del delegato contiene un oggetto valido?

+0

Se si potesse verificare se un indirizzo contenesse un oggetto valido, per definizione, lo sarebbe, perché sarebbe valido accedervi per testare? –

+1

ogg-c ha un sacco di astrazioni ... Posso immaginare un mondo in cui il runtime è stato in grado di distinguere tra un indirizzo con un oggetto valido e uno deallocato. –

risposta

10

No, non è possibile (utilmente) "verificare se un indirizzo contiene un oggetto valido". Anche se tu fossi in grado di esplorare all'interno del sistema di allocazione della memoria e determinare che il tuo indirizzo punta ad un oggetto valido, non significa necessariamente che era lo stesso oggetto a cui ti riferivi in ​​precedenza: l'oggetto potrebbe sono stati deallocati e un altro oggetto creato con lo stesso indirizzo di memoria.

Mantenere il delegato è il solito modo di risolvere questo problema. L'opzione (b) interrompe l'incapsulamento degli oggetti e potrebbe avere problemi di sicurezza dei thread.

+0

la maggior parte delle implementazioni di Apple che riesco a vedere non mantengono il delegato ... ad es. '@property (nonatomic, assign) id delegate;' sto applicando male il modello di progettazione? –

+0

Questo potrebbe essere vero per gli oggetti dell'interfaccia utente che richiedono l'interazione di tutti sul thread principale, ma penso che scoprirai che le implementazioni che eseguono operazioni asincrone nei thread in background (come NSURLConnection) manterranno il loro delegato. –

+6

Un ** delegato ** non viene MAI mantenuto! Solo ** target ** vengono mantenuti. –

1

vorrei solo scrivere

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

Se l'oggetto è valido il metodo sarà chiamato, altrimenti no. Non è necessario controllare per

self.delegate != nil 
+4

Probabilmente hai ragione sul fatto che il mio '! Self.delegate' è ridondante. Ma, so che '[delegate respondsToSelector:]' non ti protegge da 'EXC_BAD_ACCESS'. Provare a fare ciò: \t NSObject * obj = [[NSObject alloc] init]; \t [obj release]; \t for (int i = 0; i <2; i ++) { \t \t if ([obj respondsToSelector: @selector (retainCount)]) NSLog (@ "trattenere conteggio:% i", [obj retainCount]); \t} –

1

mi sono imbattuto in questa domanda perché il mio oggetto "downloader" mi stava dando EXC_BAD_ACCESS. La mia soluzione è stata la cancellazione dell'oggetto downloader giusto prima di rilasciarlo. Supponendo che usi NSURLConnection nell'oggetto downloader, chiama il metodo cancel su di esso.

È importante notare che se NSURLConnection non sta attualmente scaricando nulla, la chiamata di annullamento causerà un arresto anomalo. Avrai bisogno di una logica per verificare se è in corso un download.

1

Ho anche problemi con riferimento ai delegati deboli e attualmente ho solo una soluzione per questo problema: utilizzare il riferimento avanzato per delegato e impostare manualmente self.delegate = nil; dopo che il mio codice è completo. Questa soluzione funziona per me nel caricamento asincrono delle immagini, dove hai un ciclo di vita con finale visibile.

27

Mi sono imbattuto in questo problema e l'ho risolto. Per ARC, la soluzione è utilizzare l'attributo weak anziché assign.

L'incidente venire perché il delegato

  1. ha un attributo assign, E
  2. Ha stato deallocato.

La soluzione è quella di utilizzare l'attributo weak, perché quando i dealloca oggetto, la volontà puntatore impostare il nil. Pertanto, quando il tuo codice chiama respondsToSelector su un nil, l'Objective C ignorerà la chiamata e non si bloccherà.

Nel codice, quando si tenta di chiamare il metodo respondsToSelector su delegate, si ottiene un EXC_BAD_ACCESS. Ciò accade perché gli oggetti che utilizzano la proprietà assign non saranno impostati su nil quando vengono deallocati. (Quindi perché facendo un !self.delegate prima della respondsToSelector non impedisce il responseToSelector da essere chiamato su un oggetto deallocato, ed ancora si blocca il codice)

Come già accennato, utilizzando un attributo strong o assign su un delegato (come molti hanno detto) in ARC si tradurrà in un ciclo di conservazione. Quindi non farlo, non è necessario.

+0

oltre a usare debole, ci sono altre cose necessarie? Ad esempio, "delegate = nil" nella classe principale o impostando "id _strong delegate"? Immagino di dover affrontare lo stesso problema http://stackoverflow.com/questions/24466244/delegate-dont-exist-exc-bad-access/24970685#24970685 – Hexark

+2

Non dovresti aver bisogno di fare altro. Ho letto la tua domanda e ho notato che hai fornito una risposta che indicava che l'uso di 'weak' invece di' assign' risolve il problema. Hai ancora problemi? –

+0

Sembra che il problema sia stato risolto :) – Hexark

Problemi correlati