2009-04-26 9 views
10

Ci sono due oggetti A e B. A crea B e lo conserva. B ha una variabile di istanza che punta ad A, conservandola. Quindi entrambi si trattengono. Alcuni dicono che questa forte connessione non può essere mai più interrotta.Mantieni cicli: perché è una cosa così brutta?

Ma è davvero così?

Se B rilascia A, allora A potrebbe rilasciare facilmente B, e quindi B essere deallocato. A verrebbe deallocato non appena l'altro proprietario (suppongo ci sia qualcuno) lo rilasci.

Oppure questo problema si applica solo in un caso in cui A non crea B, ma contiene solo un forte riferimento ad esso attraverso il mantenimento in una variabile di istanza? Non vedo ancora perché questa connessione non possa essere risolta di nuovo.

risposta

15

I cicli non sono male, ma vengono spesso evitati perché possono complicare la verifica della mancanza di perdite di memoria. Le perdite si verificano soprattutto quando gli oggetti sono "conteggiati di riferimento". In una lingua o in un sistema che utilizza il conteggio dei riferimenti, un oggetto tiene traccia del numero di riferimenti che puntano su di esso. Ogni volta che viene cancellato un riferimento, il conteggio diminuisce, quando il conteggio arriva a zero, non ci sono riferimenti e quindi l'oggetto può essere cancellato.

Questo di solito si prende cura di se stesso e funziona bene senza alcun pensiero attento. Se hai un gruppo di oggetti senza cicli e trascini il tuo riferimento all'oggetto radice, allora sarà cancellato, questo significa che i riferimenti agli oggetti che possiede saranno eliminati, gli oggetti a cui si fa riferimento avranno i loro riferimenti andare a zero. Verranno cancellati e la cascata farà sì che tutti gli oggetti vengano cancellati.

Ma ... se si dispone di un ciclo, questa cascata non funziona. Potresti avere un gruppo di oggetti e non li vuoi più, quindi fai cadere l'unico riferimento che hai a questi oggetti, ma poiché c'è un ciclo gli oggetti si richiamano l'un l'altro. Ciò significa che i loro conteggi di riferimento non vanno mai a zero e non vengono eliminati. Questa è una perdita di memoria.

Chiaramente, è possibile eseguire una gestione attenta e interrompere i cicli prima di rilasciare il riferimento a un gruppo di oggetti che non si desidera più. Ma ... come ho appena detto, questo richiede un'attenta gestione. È molto facile sbagliare. Questo è uno dei motivi principali per cui si verificano perdite di memoria.

Per evitare il rischio di perdite e il difficile processo di interruzione dei cicli correttamente quando non è più necessario un gruppo di oggetti, i programmatori di solito cercano di evitare i cicli. Questo diventa più importante nei grandi progetti con molti programmatori in cui nessuno capisce l'intero sistema. Se ci fossero dei cicli, i programmatori dovrebbero stare attenti e trascorrere molto tempo studiando l'altro codice per evitare i cicli.

Alcune lingue con i garbage collector (ad esempio C#) possono eliminare un gruppo di oggetti che non sono più necessari anche se il gruppo contiene cicli.

+2

Il garbage collector Objective-C (se abilitato) può anche eliminare i gruppi di loop di conservazione (quasi tutti i garbage collector possono) ma questo non è rilevante su iPhone, dove la garbage collection di Objective-C non è supportata. –

2

Il problema è questo: A punta a e mantiene B, e B punta ae mantiene A. Quando non ci sono altri riferimenti ad A o B, non ci sarà modo di rilasciarli, perché l'app non avere alcun riferimento a loro in quel punto. Questo è chiamato ciclo di riferimento, ed è un tipo di perdita di memoria comune in qualsiasi sistema di conteggio di riferimento. Il modo in cui questo è risolto nella maggior parte dei linguaggi di alto livello consiste nell'utilizzare la garbage collection piuttosto che il conteggio dei riferimenti.

+0

Grazie. Ma perché non dovrebbero esserci altri riferimenti ad A o B? In tal caso, potrei avere due oggetti in cui la mia app non ha alcun riferimento a loro? – Thanks

+0

Se si hanno altri riferimenti ad A o B, non ci sono problemi. È solo quando perdi quei riferimenti (come se escono dallo scope) che è un problema. – Zifre

7

Un ciclo di conservazione può essere interrotto, se lo si conosce. Di solito porta a bug cattivi (perdite di memoria). Nel tuo esempio:

A* a = [[A alloc] initAndCreateB]; 

Ora, un'istanza B senza nome (creata da A) ha un conteggio resti di 1.Dal momento che teniamo un riferimento a una e l'istanza B anonima contiene un riferimento forte alla A, di un conteggio di conservare è 2.

Diciamo, abbiamo finito con A:

[a release]; 
return 12; 

Ora, una di conservare conteggio è 1. Non verrà rilasciato, la memoria è persa. Ecco perché i cicli di conservazione sono cattivi.

+0

Grazie. Per mia comprensione, non sembra che ci siano due oggetti diversi. A deve essere una Superclasse di B, in modo che la struttura dei dati di B possa rientrare in A. Ma poi, dopo averlo fatto, non sarebbe solo l'anonimo B? Potrei sbagliarmi. È tardi;) – Thanks

+0

A non deve essere una superclasse di B. Ha solo un ivar che punta a un'istanza B. La B è un oggetto separato. Pensa alle classi che vedi ogni giorno: potresti avere un NSArray e NSString e un NSDate come variabili di istanza. Quelle sono * non * sottoclassi della tua classe personalizzata. Sono classi indipendenti di cui le tue fanno uso. – Chuck

3

Il modo per interrompere un ciclo di mantenimento consiste nell'avere un metodo "chiuso" separato.

cioè

A retains B 
B retains A 

quando hai finito, chiamare un metodo (lo chiamerò "close") su un dove A rilascia B. È quindi possibile rilasciare A e l'intero ciclo rilascerà (supponendo che non ci siano ritiri altrove).

Problemi correlati