2010-06-02 13 views
5

Ho notato di recente un arresto anomalo in una delle mie app quando un oggetto ha tentato di inviare un messaggio al suo delegato e il delegato era già stato rilasciato.Verifica se è stato rilasciato un oggetto prima di inviarlo a un messaggio

Al momento, poco prima di chiamare qualsiasi metodo delegato, ho eseguito questo controllo:

if (delegate && [delegate respondsToSelector:...]){ 
    [delegate ...]; 
} 

Ma ovviamente questo non tiene conto se il delegato non è pari a zero, ma è stato deallocato.

Oltre a impostare il delegato dell'oggetto su zero nel metodo dealloc del delegato, c'è un modo per controllare se il delegato è già stato rilasciato solo in caso non abbia più un riferimento all'oggetto.

+4

'if (delegato)' è ridondante - '[delegate respondsToSelector:]' sarà false se 'delegate' è nil. – shosti

+0

Punto interessante. Non ci avevo pensato prima. –

risposta

16

No. Non è possibile stabilire se una variabile punta a un oggetto valido. Devi strutturare il tuo programma in modo che il delegato di questo oggetto non stia andando via senza prima farlo sapere.

+2

Grazie. Dal momento del crash è stata una semplice questione di impostare il delegato dell'oggetto su zero nel dealloc del delegato, ma mi ha fatto pensare in generale :) –

0

come utilizzare un contatore che si incrementa ogni volta che si assegna e si decrementa ogni volta che si dealloc. In questo modo è possibile rilevare i doppi allocamenti e decidere di non utilizzare un delegato se il contatore non è nullo ma l'indirizzo non è nullo anche

+0

Questa non è una buona idea, purtroppo. Gli oggetti tracciano il proprio conteggio di ritenzione, che è il modo in cui sono "automaticamente" deallocati dopo l'ultima versione. Dal momento che non mantiene il delegato, questo in realtà non sarebbe di grande aiuto. –

+0

sì, è stato un tentativo lungo, ma volevo descrivere in che modo il riferimento e il dereferenziamento sono gestiti in background su alcuni framework –

+0

Dopo due anni ciò che @tom ha chiesto, lo trovo molto utile. Mi sono imbattuto nello stesso scenario e la soluzione migliore sarebbe dichiarare la mia proprietà delegata come "debole", purtroppo ho bisogno di renderla compatibile con le versioni precedenti (dovrebbe supportare anche gli obiettivi di distribuzione di iOS 4). Quindi la mia ipotesi è di andare su Blocks invece che su Delegates, funzionerà? (Uso i delegati per scaricare le immagini in background e richiamare il callback del metodo delegate una volta completato, posso fare lo stesso con Blocks. Non sono sicuro che risolverà il problema dietro la chiamata degli oggetti deallocati) – chathuram

8

Suppongo che tu non stia utilizzando GC. In tal caso, la convenzione standard prevede che il codice che imposta il delegato sia responsabile dell'impostazione del riferimento dell'utente delegato a nil prima di consentire la delocalizzazione del delegato. Se si utilizza GC, è possibile utilizzare un riferimento __weak per il delegato, consentendo al garbage collector di impostare il riferimento su nil quando l'istanza viene eliminata.

+0

+1 per aver richiamato i riferimenti '__weak' e li ho raccomandati nel contesto di GC. In effetti, si può sostenere che l'uso di "__weak" in questo caso è la cosa giusta da fare, se (quando?) Il codice verrà in seguito convertito in GC in futuro. –

+0

esiste ancora __unsafe_unretained per il supporto legacy, ed è piuttosto difficile da usare come ARC non assegna valore nullo agli oggetti __unsafe_unretain raccolti. –

0

per debug propone è possibile eseguire l'override del metodo di rilascio sulla classe per vedere quando viene chiamato.

-(oneway void)release 
{ 
    NSLog(@"release called"); 
    [super release]; 
} 
Problemi correlati