Che tipo di perdite il conteggio automatico dei riferimenti in Objective-C non impedisce o riduce al minimo?


235

Nelle piattaforme Mac e iOS, le perdite di memoria sono spesso causate da puntatori inediti. Tradizionalmente, è sempre stato della massima importanza controllare i tuoi alloc, copie e conservazioni per assicurarsi che ognuno abbia un messaggio di rilascio corrispondente.

La toolchain fornita con Xcode 4.2 introduce il conteggio dei riferimenti automatici (ARC) con l'ultima versione del compilatore LLVM , che risolve completamente questo problema facendo in modo che il compilatore gestisca la memoria per te. È piuttosto interessante e riduce molti inutili e banali tempi di sviluppo e previene molte perdite di memoria trascurate che sono facili da correggere con un corretto equilibrio di mantenimento / rilascio. Anche i pool di rilascio automatico devono essere gestiti in modo diverso quando si abilita ARC per le app Mac e iOS (poiché non è più necessario allocare le proprie NSAutoreleasePool).

Ma quali altre perdite di memoria non impedisce che devo ancora fare attenzione?

Come bonus, quali sono le differenze tra ARC su Mac OS X e iOS e Garbage Collection su Mac OS X?

Risposte:


262

Il problema principale legato alla memoria di cui dovrai ancora essere a conoscenza è il mantenimento dei cicli. Ciò si verifica quando un oggetto ha un puntatore forte su un altro, ma l'oggetto target ha un puntatore forte sull'originale. Anche quando tutti gli altri riferimenti a questi oggetti vengono rimossi, si aggrapperanno l'uno all'altro e non verranno rilasciati. Questo può accadere anche indirettamente, da una catena di oggetti che potrebbe avere l'ultimo nella catena che fa riferimento a un oggetto precedente.

È per questo motivo che esistono i qualificatori di proprietà __unsafe_unretainede __weak. Il primo non manterrà alcun oggetto a cui punta, ma lascia aperta la possibilità che quell'oggetto vada via e che punti a cattiva memoria, mentre il secondo non mantiene l'oggetto e si imposta automaticamente su zero quando il suo bersaglio viene deallocato. Dei due, __weakè generalmente preferito su piattaforme che lo supportano.

Utilizzeresti questi qualificatori per cose come i delegati, in cui non vuoi che l'oggetto mantenga il suo delegato e potenzialmente porti a un ciclo.

Un'altra coppia di problemi significativi legati alla memoria sono la gestione degli oggetti Core Foundation e la memoria allocata usando malloc()per tipi come char*. ARC non gestisce questi tipi, solo oggetti Objective-C, quindi dovrai comunque gestirli da solo. I tipi di Core Foundation possono essere particolarmente complicati, perché a volte devono essere collegati tra loro per abbinare gli oggetti Objective-C e viceversa. Ciò significa che il controllo deve essere trasferito avanti e indietro da ARC quando si collega tra tipi CF e Objective-C. Sono state aggiunte alcune parole chiave correlate a questo ponte e Mike Ash ha una grande descrizione di vari casi di ponte nel suo lungo write-up ARC .

Oltre a questo, ci sono molti altri casi meno frequenti, ma ancora potenzialmente problematici, nei quali la specifica pubblicata viene approfondita.

Gran parte del nuovo comportamento, basato sul mantenere gli oggetti in giro fino a quando c'è un forte puntatore ad essi, è molto simile alla garbage collection sul Mac. Tuttavia, le basi tecniche sono molto diverse. Piuttosto che avere un processo di garbage collector che viene eseguito a intervalli regolari per ripulire gli oggetti a cui non viene più indicato, questo stile di gestione della memoria si basa sulle rigide regole di conservazione / rilascio a cui tutti dobbiamo obbedire in Objective-C.

ARC prende semplicemente le ripetitive attività di gestione della memoria che abbiamo dovuto svolgere per anni e le scarica nel compilatore, così non dovremo più preoccuparci di esse. In questo modo, non si hanno problemi di arresto o profili di memoria a dente di sega riscontrati su piattaforme di raccolta rifiuti. Ho sperimentato entrambi questi nelle mie applicazioni Mac raccolte di rifiuti e sono ansioso di vedere come si comportano sotto ARC.

Per ulteriori informazioni sulla garbage collection vs. ARC, vedi questa risposta molto interessante di Chris Lattner nella mailing list di Objective-C , dove elenca molti vantaggi di ARC rispetto a Garbage Collection 2.0. Ho incontrato alcuni dei problemi di GC che descrive.


2
Grazie per la risposta dettagliata. Ho avuto lo stesso problema in cui ho definito un delegato in _unsafe_unretained e ho fatto in modo che la mia applicazione si arrestasse in modo anomalo, successivamente risolto cambiando in forte ma ora ha una perdita di memoria. Quindi, l'ho cambiato in debole e funziona come un fascino.
Chathuram,

@ichathura Wow! Mi hai salvato dal fango dell'ARCO. Ho riscontrato lo stesso arresto anomalo durante l'utilizzo di CMPopTipView.
Nianliang,

@BradLarson: "non si hanno problemi di arresto o profili di memoria a dente di sega riscontrati su piattaforme di raccolta rifiuti". Mi aspetterei peggiori profili di memoria di arresto e dente di sega dal recupero basato sull'ambito e prestazioni molto peggiori dal conteggio dei riferimenti, quindi mi piacerebbe vedere un vero confronto.
Jon Harrop,

Brad, il link di Chris Lattner è morto . Non sono al 100% ma ho trovato questo altro link. Che penso sia quello a cui volevi collegarti: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/…
Honey

1
@ Ciao - Grazie per averlo sottolineato. Quello che colleghi è leggermente diverso, ma ho sostituito il collegamento morto con una versione archiviata del messaggio originale. È negli archivi delle mailing list, che dovrebbero essere disponibili da qualche parte, ma cercherò di trovare la loro nuova posizione.
Brad Larson

14

ARC non ti aiuterà con la memoria non ObjC, ad esempio se hai malloc()qualcosa, ne hai ancora bisogno free().

ARC può essere preso in giro performSelector:se il compilatore non riesce a capire quale sia il selettore (il compilatore genererà un avviso su questo).

ARC genererà anche codice seguendo le convenzioni di denominazione di ObjC, quindi se mescoli il codice ARC e MRC puoi ottenere risultati sorprendenti se il codice MRC non fa ciò che il compilatore pensa che i nomi promettano.


7

Ho riscontrato perdite di memoria nella mia applicazione a causa dei seguenti 4 problemi:

  1. NSTimer non invalidante quando si eliminano i controller di visualizzazione
  2. Dimenticare di rimuovere eventuali osservatori a NSNotificationCenter quando si elimina il controller di visualizzazione.
  3. Mantenere forti riferimenti a se stessi in blocchi.
  4. Utilizzo di riferimenti forti ai delegati nelle proprietà del controller di visualizzazione

Fortunatamente mi sono imbattuto nel seguente post di blog e sono stato in grado di correggerli: http://www.reigndesign.com/blog/debugging-retain-cycles-in-objective-c-four-likely-culprits/


0

ARC non gestirà inoltre i tipi CoreFoundation. Puoi "collegarli" (usando CFBridgingRelease()) ma solo se lo utilizzerai come oggetto Objective-C / Cocoa. Si noti che CFBridgingRelease riduce semplicemente il conteggio di mantenimento di CoreFoundation di 1 e lo sposta sull'ARC di Objective-C.


0

Xcode 9 offre un ottimo strumento per trovare quel tipo di problemi. Si chiama: " Debug Memory Graph ". Usandolo puoi trovare il tuo oggetto trapelato per tipo di classe e puoi vedere chiaramente chi detiene un forte riferimento ad esso, rilasciandolo da lì risolve il tuo problema. Rileva anche i cicli di memoria.

Vedi maggiori informazioni su come usarlo

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.