In precedenza abbiamo trascurato l'invocazione del metodo release
sugli oggetti, necessaria per una corretta gestione della memoria i cui concetti principali verranno introdotti di seguito. I dispositivi iOS non posseggono un Garbage Colllector il quale gestirebbe automaticamente la memoria dell'intero dispositivo, ma bensì delega agli sviluppatori una gestione della memoria "manuale" nello sviluppo delle loro applicazioni.
Fulcro della gestione della memoria è una proprietà che ogni oggetto possiede: il retainCount
. Quest'ultimo indica il numero di riferimenti associati all'oggetto e quando il retainCount
di qualsiasi oggetto arriva a 0 quest'ultimo viene deallocato.
Questa operazione viene effettuata da un oggetto chiamato AutoreleasePool che è istanziato non appena l'applicazione viene eseguita e si occupa di monitorare i retainCount
degli oggetti all'interno dell'applicazione per deallocare quelli con valore 0.
Ma in che modo cambia il valore del retainCount
? Quando un qualsiasi oggetto viene allocato ed inizializzato con i classici metodi alloc
e init
, il suo retainCount
ha valore 1. Per modificare in maniera esplicita il retainCount
di un oggetto allocato si possono usare due metodi:
retain
: incrementa ilretainCount
di 1.release
: decrementa ilretainCount
di 1
Ritornando alla nostra "applicazione" di prova Hello World, e facendo un rapido conto del retainCount
della label, abbiamo la sua inizializzazione (retainCount
= 1) e poco dopo una chiamata al metodo release
il quale, decrementando di uno il retainCount
, verrebbe riportato a 0 e la label verrebbe deallocata.
Ma perché l'applicazione funziona correttamente anche se la label ha retainCount
zero? Semplicemente perchè il retainCount
non è 0; infatti, ricordando la definizione del retainCount
(ossia che rappresenta il numero di riferimenti all'oggetto), l'invocazione del metodo addSubview
sulla view del View Controller incrementa di 1 il retainCount
della label (quindi dopo la chiamata a quel metodo il retainCount
vale 2) in quanto aggiunge quest'ultima alla propria gerarchia ed ha bisogno di un riferimento esplicito alla label stessa. Infine con la chiamata al metodo release
riportiamo il retainCount
ad 1.
Ma quando verrà deallocata la label? Ciò avverrà quando il firstViewController
verrà deallocato e dunque, non essendo più necessario il riferimento alla label, il suo retainCount
verrà decrementato di 1 (assumendo dunque valore 0) e, finalmente, anche la label verrà deallocata.
Come abbiamo detto prima, dopo l'invocazione del metodo addSubView
il retainCount della label viene portato a 2; risulta dunque indispensabile la chiamata esplicita al metodo release
per riportare il retainCount
dela label a 1 per poter essere deallocata nello scenario descritto poco sopra.
Errata gestione della memoria: leak e crash
Una errata gestione della memoria può portare ad uno dei seguenti problemi: un leak di memoria o un crash dell'applicazione.
Il primo problema è legato ad un retainCount
di un oggetto troppo elevato. Tale oggetto non verrà mai deallocato (in quanto possiede un retainCount
maggiore di 1) causando quindi uno "spreco" di memoria.
Il secondo problema (il crash dell'applicazione), è causato principalmente da un retainCount
troppo basso su un oggetto che causa una deallocazione prematura dell'oggetto stesso. Di per se, la deallocazione prematura di un oggetto non causa il crash dell'applicazione, ma se per esempio proviamo ad invocare un metodo su tale oggetto l'applicazione crasherà dato che si sta provando ad accedere ad un'area di memoria vuota.
Analizzando i due scenari il primo è sicuramente meno critico, ma bisogna fare attenzione: se il numero di leak è elevato si rischia che l'applicazione uccupi una quantità di memoria eccessiva e, superata una certa soglia, il sistema operativo del dispositivo terminerà esso stesso l'applicazione.
ARC (Automatic Reference Counting)
Con il rilascio di Xcode 4.2 è stato introddo ARC, una funzionalità molto interessante che semplifica la vita agli sviluppatori. Utilizzando ARC lo sviluppatore non è più costretto ad invocare esplicitamente i metodi retain
e release
per mantenere un valore coerente del retainCount dell'oggetto, dato che queste direttive vengono inserite, a compile-time da ARC stesso. ARC dunque non è un garbage collector in quanto non lavora a run-time.
Nel tutorial pratico che presenteremo in questa guida utilizzeremo proprio ARC e dunque il lettore non troverà più riferimenti espliciti a metodi di retain
e release
.