Gestione della memoria

La prima domanda che la maggior parte degli sviluppatori Java si pone è come la gestione della memoria viene implementata da J2ObjC, dato che Java ha una garbage collection, mentre Objective-C non la esegue per impostazione predefinita. iOS dispone di due metodi di gestione della memoria: il conteggio dei riferimenti e il conteggio automatico dei riferimenti (ARC).

J2ObjC genera un codice di gestione della memoria diverso, a seconda del metodo scelto. Traduci con l'opzione -use-arc per generare codice che utilizza ARC. Per impostazione predefinita, viene utilizzato il conteggio manuale dei riferimenti.

Conteggio riferimenti

Il metodo di conteggio dei riferimenti rende esplicita la proprietà degli oggetti. Un metodo è proprietario di un oggetto quando lo crea, finché non lo rilascia. Quando si riceve un oggetto da un altro metodo, il metodo di ricezione conserva l'oggetto se ha bisogno di riferimento dopo la restituzione del metodo. Quando un metodo non ha più bisogno di fare riferimento a un oggetto, deve rilasciarlo. Quando il conteggio delle conservazioni di un oggetto è zero, la sua memoria viene liberata e l'oggetto non è più valido. Quando gli oggetti vengono liberati, il metodo dealloc() viene chiamato per rilasciare la proprietà delle variabili di istanza.

Un problema di questa tecnica è il modo in cui trasferire la proprietà di un oggetto. Ad esempio, un metodo di fabbrica potrebbe essere chiamato per creare un oggetto. Se il metodo di fabbrica rilascia l'oggetto prima di restituirlo (dato che non vuole più essere proprietario dell'oggetto), l'oggetto viene liberato prima che il metodo chiamante possa conservarlo.

Per trasferire la proprietà di un oggetto, un metodo gli invia un messaggio di rilascio automatico (anziché un messaggio di rilascio), che reindirizza il messaggio di rilascio. Ciò consente al metodo di fabbrica di creare un oggetto da restituire e di rinunciare alla sua proprietà senza invalidare l'oggetto. A intervalli regolari (ad esempio dopo ogni iterazione di loop di eventi in un'applicazione iOS), il pool di rilascio automatico viene "svuotato", il che significa che a tutti gli oggetti nel pool vengono inviati i messaggi di release differita. Tutti gli oggetti il cui numero di registrazioni scendono a zero vengono liberati normalmente.

Poiché il carico sulla gestione della memoria spetta allo sviluppatore, è facile perdere memoria con il metodo di conteggio dei riferimenti. Tuttavia, Apple consiglia alcune best practice per ridurre al minimo questo problema, implementate da J2ObjC.

Sono inoltre disponibili il supporto per runtime e strumenti per rilevare le perdite di memoria. Il runtime Objective-C segnala eventuali perdite rilevate all'uscita di un'applicazione, il che è uno dei motivi per cui J2ObjC converte i test JUnit in programmi binari eseguibili. Xcode utilizza Clang e il compilatore dispone di eccellenti analisi statiche per problemi di memoria, che Xcode rende disponibile tramite il comando Analizza.

Conteggio automatico dei riferimenti (ARC)

ARC è il metodo di gestione della memoria consigliato da Apple. Sposta la responsabilità del conteggio dei riferimenti al compilatore, aggiungendo così i metodi di conservazione, rilascio e rilascio automatico appropriati durante la compilazione. ARC supporta i riferimenti deboli per i dispositivi che eseguono iOS 5 e versioni successive.

Consigliamo ai progetti di utilizzare ARC per il codice tradotto. Il codice Objective-C con transpile è simile al codice Objective-C scritto a mano. L'utilizzo di ARC potrebbe aiutare gli sviluppatori a evitare errori comuni relativi alla memoria, come rilascio eccessivo o sottoriferimento, rendendo la gestione della memoria più semplice e meno soggetta a errori.

Tieni presente che ARC non è sicuro per eccezioni per impostazione predefinita. Nello specifico, quando vengono generate delle eccezioni, perde la memoria. Poiché le eccezioni sono più comuni in Java e di solito sono recuperabili, questo può essere problematico. L'utilizzo di -fobjc-arc-exceptions durante la compilazione con l'arco risolverà le perdite con un costo in termini di prestazioni accettabile.

Consigliamo inoltre ai nuovi progetti di utilizzare ARC per il codice Objective-C scritto a mano e di ricorrere al conteggio manuale del riferimento solo se i dati di profilazione mostrano un problema di prestazioni reale. Sia il codice ARC che quello non ARC possono essere compilati e collegati nella stessa app senza problemi.

Riferimenti deboli

I campi possono essere annotati con com.google.devtools.j2objc.Weak, che il transpiler utilizza per generare campi che seguono la semantica di riferimento debole di Objective-C. Quando utilizzi il conteggio dei riferimenti, significa che il campo non viene conservato all'inizializzazione, ma viene rilasciato automaticamente quando viene rilasciata l'istanza contenitore. Con ARC, i campi deboli sono contrassegnati con l'annotazione __unsafe_unretained e le relative proprietà vengono dichiarate deboli.

In alcuni casi, un'istanza di classe interna entra in un ciclo di riferimento con la sua istanza esterna. In questo caso, un'annotazione com.google.devtools.j2objc.WeakOuter viene utilizzata per contrassegnare la classe interna, quindi il riferimento alla classe esterna viene trattato come descritto sopra. Gli altri campi nella classe interna non sono interessati da questa annotazione.

J2ObjC supporta anche la classe WeakReference, quindi il codice Java che la utilizza funzionerà allo stesso modo quando viene tradotto. Tieni presente che WeakReference è intrinsecamente non deterministico sulla JVM; le applicazioni che vogliono evitare perdite di memoria mantenendo la prevedibilità dovrebbero invece preferire @Weak e @WeakOuter.

Strumenti di gestione della memoria

  • Cycle Finder: analizza i file di origine Java per individuare cicli di riferimento degli oggetti efficaci.
  • Xcode Instruments: la suite di strumenti di profilazione di Xcode.
  • Xcode Memory Diagnostica: crea opzioni per l'esecuzione con diagnostica e logging della memoria.