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.