{"id":4010,"date":"2010-07-14T14:42:54","date_gmt":"2010-07-14T12:42:54","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=4010"},"modified":"2010-07-14T14:42:54","modified_gmt":"2010-07-14T12:42:54","slug":"l012-gestione-della-memoria-durante-lo-sviluppo-di-applicazioni-iphone-e-ipad","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/l012-gestione-della-memoria-durante-lo-sviluppo-di-applicazioni-iphone-e-ipad\/","title":{"rendered":"L#012 &#8211; Gestione della memoria durante lo sviluppo di applicazioni iPhone e iPad"},"content":{"rendered":"<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/07\/XCode_150x150.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/07\/XCode_150x150.jpg\" alt=\"Logo Xcode\" title=\"XCode_150x150\" width=\"150\" height=\"150\" class=\"alignleft size-full wp-image-4016\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/07\/XCode_150x150.jpg 150w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/07\/XCode_150x150-64x64.jpg 64w\" sizes=\"auto, (max-width: 150px) 100vw, 150px\" \/><\/a> Oggi parliamo di gestione della memoria nelle applicazioni iPhone (e iPad). Dopo un <a href=\"http:\/\/www.devapp.it\/wordpress\/l008-%E2%80%93-objective-c-parte-iii.html\" target=\"_blank\">primo ottimo articolo<\/a> sull&#8217;argomento, creato dal nostro Ignazio Cal\u00f2, abbiamo pensato fosse meglio, viste le numerose richieste, spendere ancora un paio di parole su questo tema di vitale importanza per lo sviluppo di applicazioni per dispositivi mobili come appunto iPhone, iPod Touch e iPad.<\/p>\n<h4>Perch\u00e8 \u00e8 cos\u00ec importante la gestione della memoria?<\/h4>\n<p>Gestire la memoria significa prima di tutto evitare di sprecare inutilmente risorse (in questo caso di memoria), che, soprattutto nel caso dei dispositivi mobili, ritroviamo in quantit\u00e0 limitata e non espandibile. Ovviamente gestire la memoria non \u00e8 solo importante per questo tipo di device, ma anche per le applicazioni desktop, le cui funzionalit\u00e0 potrebbero portare ad occupare enormi quantit\u00e0 di memoria (pensate ad esempio ad applicazioni di tipo scientifico). Quanto mostreremo in questo articolo sar\u00e0 utile quindi non solo per lo sviluppo di applicazioni iPhone e iPad, ma anche per quello di applicazioni MacOS.<!--more--><\/p>\n<p>Come saprete (o comunque intuirete) ogni variabile, puntatore, oggetto o altro elemento del nostro programma occupa memoria e anche se potrebbero sembrarvi quantit\u00e0 minime, sappiate che l&#8217;apparenza, spesso, inganna. Perch\u00e8 dico questo? Objective-C (il linguaggio principale con cui lavoriamo) \u00e8 un linguaggio orientato agli oggetti, e questa potente tecnica di programmazione, come sapete, ci offre funzionalit\u00e0 potentissime, ma allo stesso tempo molto pericolose se non capiamo a fondo il loro principio di funzionamento. Una di queste potenti (e pericolose) caratteristiche \u00e8 l&#8217;ereditariet\u00e0, ovvero la caratteristica che ci permette di creare nuovi oggetti personalizzati, partendo da altri esistenti mantenendo tutte le carattaristiche (propriet\u00e0 e metodi) dell&#8217;oggetto di partenza.<\/p>\n<p>Ora, come ben saprete la maggior parte del nostro lavoro su iPhone e iPad si basa sul framework Cocoa Touche in particolare dall&#8217;UIKit, offerto da Apple, (<a href=\"http:\/\/www.devapp.it\/wordpress\/introduzione-al-framework-uikit.html\" target=\"_blank\">maggiori informazioni qui<\/a>) e come potete vedere dal seguente schema esiste una gerarchia specifica per gli oggetti di questo utile kit, in particolare notate come tutti gli oggetti siano derivati di un unico &#8220;super&#8221; oggetto: NSObject.<\/p>\n<p><center><br \/>\n<a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2009\/11\/uikit.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2009\/11\/uikit.jpg\" alt=\"UIKit\" title=\"uikit\" width=\"345\" height=\"402\" class=\"aligncenter size-full wp-image-802\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2009\/11\/uikit.jpg 690w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2009\/11\/uikit-257x300.jpg 257w\" sizes=\"auto, (max-width: 345px) 100vw, 345px\" \/><\/a><br \/>\n<\/center><\/p>\n<p>Ora immaginiamo un semplice caso, dobbiamo creare una nuova classe con metodi ed eventi personalizzati, che eredita direttamente da NSObject. Analizzando il file di interfaccia della nostra nuova classe, potremmo trovarci di fronte a qualcosa di simile:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n@interface MyClass: NSObject {\r\n    IBOutlet UITextField *myTextField;\r\n}\r\n\/\/ Eventuali metodi\r\n@end\r\n<\/pre>\n<p>Istanziato un oggetto di questa classe, come minimo dovremo aver sufficiente spazio in memoria da allocare per contenere un oggetto UITextField, ma non solo. Dato che questa classe eredita da NSObject, dobbiamo aver spazio sufficiente per contenere anche tutti gli elementi di questo oggetto!<\/p>\n<p>Immaginate ora di creare un nuovo oggetto partendo invece da UITextField, in questo caso dovrete prevedere di aver spazio in memoria per i nuovi elementi e, secondo lo schema sopra, anche per gli elementi di UITextField (da cui ereditate direttamente), ma anche quelli di UIControl (da cui eredita UITextField), UIView (da cui eredita UIControl), UIResponder (da cui eredita UIView) e infine NSObject. Insomma, come potrete intuire le cose si complicano un pochino e di fatto, se pensavate di cavarvela con poca memoria, in realt\u00e0 ci rendiamo conto che ne serve pi\u00f9 del previsto e dovremo allocare la quantit\u00e0 di memoria necessaria a contenere sia i nuovi elemente da noi creati che tutti quelli ereditati. Oltre ad allocare la quantit\u00e0 corretta di memoria, capirete quindi che sar\u00e0 il caso, inoltre, di liberare appena possibile la memoria utilizzata, ovvero quando l&#8217;oggetto residente in memoria non serve pi\u00f9.  <\/p>\n<p>Insomma, un attenta pianificazione e gestione della memoria pu\u00f2 essere decisiva per il successo della vostra applicazione iPhone!<\/p>\n<h4>Allocare la giusta quantit\u00e0 di memoria per un oggetto in Objective-C (alloc, init)<\/h4>\n<p>Allocare la corretta quantit\u00e0 di memoria (ricordate ne che abbiamo una quantit\u00e0 limitata da usare), sembrerebbe quindi un processo lungo e complesso, ma non \u00e8 cos\u00ec. Il framework, e in particolare proprio il &#8220;super&#8221; oggetto NSObject, ci offre infatti un metodo di classe molto utile: <strong>Alloc<\/strong>!<\/p>\n<p>Grazie a questo metodo (che potremo usare nei nostri oggetti che ereditano direttamente o indirettamente da NSObject) saremo in grado di allocare la giusta quantit\u00e0 di memoria sia per tutti i nostri nuovi elementi. che per quelli ereditati. A livello di codice \u00e8 molto semplice, se dovessimo creare nel nostro programma un&#8217;istanza di MyClass, questo potrebbe essere il codice da scrivere:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n    MyClass *myNewClass = [MyClass alloc];\r\n<\/pre>\n<p>L&#8217;istruzione \u00e8 ancora incompleta, infatti dobbiamo ancora inizializzare il nostro oggetto perch\u00e8 sia pronto per essere utilizzato all&#8217;interno del nostro programma. Per farlo possiamo sfruttare un altro metodo offerto da NSObject: <strong>init<\/strong>!<br \/>\nGrazie ad &#8220;init&#8221; potremo appunto inizializzare i nostri oggetti, ovvero dotarlo della configurazione di default che servir\u00e0 all&#8217;avvio dell&#8217;applicazione (o meglio al momento della creazione dell&#8217;oggeto). Anche qui il codice \u00e8 molto semplice:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n    MyClass *myNewClass = [[MyClass alloc] init];\r\n<\/pre>\n<p>Unica cosa che dobbiamo ricordare \u00e8 che come lo spazio da allocare deve essere calcolato anche per gli elementi degli oggetti da cui si eredita, anche l&#8217;inizializzazione deve essere fatta per questi stessi elementi. Niente paura, anche per questo ci baster\u00e0 un&#8217;unica riga di codice all&#8217;interno del metodo init nell&#8217;implementazione della nostra classe:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (id)init {\r\n    [super init];\r\n    \/\/ Codice per inizializzazione\r\n   return self;\r\n}\r\n<\/pre>\n<p>Grazie all&#8217;istruzione [super init] non ci dovremo preoccupare di inizializzate tutto manualmente.<br \/>\nA questo punto il nostro oggetto &#8220;myNewClass &#8221; \u00e8 pronto per essere utilizzato.<\/p>\n<h4>Liberare la memoria al momento giusto (retain, release, dealloc)<\/h4>\n<p>Per la creazione del nostro oggetto nel programma \u00e8 tutto, \u00e8 infatti stata creata un&#8217;istanza della nostra classe, la abbiamo allocata in memoria (considerando lo spazio necessario) e infine la abbiamo inizializzata a dovere per poterla utilizzare come meglio crediamo. Ovviamente la gestione della memoria non si limita a questo, dovremo infatti prevedere, ad un certo punto, di liberare lo spazio di memoria occupato dalla stessa. NSObject offre un metodo che compie questo lavoro: <strong>dealloc<\/strong>, questo, per\u00f2, non dovr\u00e0 mai essere chiamato esplicitamente, ma dovr\u00e0 essere chiamato indirettamente dal metodo di protocollo release. Non preoccupatevi se non capite a fondo cosa sto dicendo, ora vedr\u00f2 di spiegarvi il meccanismo che sta dietro questa frase.<\/p>\n<p>Perch\u00e8 non possiamo invocare direttamente dealloc? Semplicemente perch\u00e8 deallocando l&#8217;oggetto sparirebbe dall&#8217;intero programma e potrebbe capitare che in realt\u00e0, questo, serva ancora da qualche parte. Infatti oltre a servire nel metodo in cui \u00e8 chiamato, un oggetto potrebbe essere passato come argomento ad altro oggetto e avere ancora uno scopo che necessiti la sua esistenza. Deallocandolo in un caso come questo il programma restituirebbe un errore o comunque si comporterebbe in modo anomalo e imprevisto. <\/p>\n<p>Da qui nasce l&#8217;esigenza avere un contatore che indichi al nostro programma se esiste ancora qualcuno interessato ad sfruttare il nostro oggetto. Questo contatore dovr\u00e0 avere valore 1 non appena l&#8217;oggetto verr\u00e0 allocato in memoria, dovr\u00e0 quindi essere incrementato di 1 ogni volta che un altro oggetto ha interesse a mantenerlo in memoria, e diminuito di 1 quando un oggetto decide che non gli serve pi\u00f9. <\/p>\n<p>Anche in questo caso non dovremmo inventarci nulla in quanto in nostro aiuto arriva il nostro caro NSObject, che offre ad ogni oggetto da esso ereditato una propriet\u00e0 chiamata <strong>retainCount<\/strong>. Quando retainCount varr\u00e0 0, <em>dealloc<\/em> verr\u00e0 richiamato in automatico e la memoria sar\u00e0 liberata.<\/p>\n<p>Come detto allocando un oggetto in memoria verr\u00e0 impostato il suo <em>retainCount<\/em> a 1, noi dovremo semplicemente dire al programma di sottrargli un 1 quando non ci servir\u00e0 pi\u00f9, per farlo useremo il metodo <em>release<\/em>:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\nMyClass *myNewClass = [[MyClass alloc] init];\r\n\r\n\/\/ Facciamo qualcosa con la nostra myNewClass\r\n\r\n[myNewClass release];\r\n<\/pre>\n<p>In particolare riportiamo i momenti in cui <em>retainCount<\/em> pu\u00f2 variare (tratti dal nostro <a href=\"http:\/\/www.devapp.it\/wordpress\/l008-%E2%80%93-objective-c-parte-iii.html\" target=\"_blank\">precedente articolo<\/a> su questo tema a cura di Ignazio Cal\u00f2):<\/p>\n<ul>\n<li>Quando una variabile viene inizializzata il suo retainCount viene incrementato di 1.<\/li>\n<li>I metodi alloc, copy, allocWithZone incrementano retainCount di 1.<\/li>\n<li>I metodi release, autorelease decrementano retainCount di 1. (autorelease lo fa in un momento non deterministico)<\/li>\n<li>Se non diversamente specificato il metodo autorelease viene invocato di default.<\/li>\n<\/ul>\n<h4>Casi particolari<\/h4>\n<p>Ora, senza entrare troppo nel dettaglio, sappiate che esistono alcuni casi in cui possiamo usare un oggetto senza allocarlo o deallocarlo, semplicemente il tutto viene gestito in automatico e gli oggetti vengono rilasciati quando non servono pi\u00f9.<\/p>\n<p>Vediamo un esempio:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\nNSString *myNewString = [NSString stringWithFormat:@\"Nuova stringa\"];\r\n<\/pre>\n<p>In questo caso stiamo utilizzando il metodo di classe &#8220;stringWithFormat&#8221; (quelli che iniziano con + per intenderci), per questo tipo di metodi non dovremo preoccuparci di nulla e potremo utilizzare questi oggetti senza troppi pensieri.<\/p>\n<h4>Errori nella gestione della memoria<\/h4>\n<p>Per concludere vediamo cosa comporta non gestire correttamente la memoria durante lo sviluppo di un&#8217;applicazione iPhone, iPad e anche Mac. Se allochiamo un oggetto, dedichiamo quindi ad esso uno spazio in memoria, ma ci dimentichiamo per qualche ragione di rilasciarlo, questo occuper\u00e0 per tutta la durata dell&#8217;esecuzione del programma quello spazio in memoria, e ogni volta che richiameremo una nuova istanza dell&#8217;oggetto, un altro pezzettino di memoria andr\u00e0 occupato inutilmente. Capirete che pian piano, la memoria disponibile (ricordiamo che sui dispositivi mobili \u00e8 limitata in modo considerevole rispetto agli ambienti desktop), andr\u00e0 ad esaurirsi fino al blocco del programma stesso o al verificarsi di comportamenti anomale e imprevisti. Questo tipo di errore nella gestione della memoria \u00e8 chiamato <strong>Memory Leak<\/strong>.<\/p>\n<p>Per farvi comprendere meglio cosa significa incappare in questo errore vi riportiamo, per semplicit\u00e0 e comodit\u00e0, e vista inoltre l&#8217;immediatezza e la chiarezza di quanto illustrato, un breve esempio <a href=\"http:\/\/it.wikipedia.org\/wiki\/Memory_leak\" target=\"_blank\">tratto da wikipedia<\/a>:<\/p>\n<blockquote><p>\n<em>Questo esempio vuole dimostrare come un leak pu\u00f2 nascere, ed i suoi effetti, senza dover conoscere le basi della programmazione. Questo \u00e8 solo un esempio fittizio.<\/p>\n<p>Il programma in questione fa parte di un software molto semplice dedicato al controllo di un ascensore. Questa porzione di algoritmo viene eseguita ogni volta che qualcuno all&#8217;interno preme un bottone.<\/p>\n<p>Quando il bottone viene premuto:<\/p>\n<ul>\n<li>recupera un po&#8217; di memoria per ricordare il piano richiesto<\/li>\n<li>metti il numero richiesto in memoria<\/li>\n<li>siamo gi\u00e0 al piano giusto?<\/li>\n<li>se s\u00ec, non dobbiamo fare niente: finito<\/li>\n<li>altrimenti, aspetta finch\u00e9 l&#8217;ascensore \u00e8 disponibile<\/li>\n<li>vai al piano richiesto<\/li>\n<li>rilascia la memoria usata per ricordare il numero del piano<\/li>\n<\/ul>\n<p>Questo programma potrebbe sembrare corretto, ma contiene un leak. Consideriamo il caso in cui l&#8217;ascensore si trova al piano 3 e premiamo il bottone 3. Otteniamo un po&#8217; di memoria che non restituiremo mai. Ogni volta che succede perdiamo un po&#8217; di memoria.<\/p>\n<p>Questo problema non avr\u00e0 un effetto immediato. Le persone non premono il bottone di un piano su cui stanno, ed in ogni caso ci pu\u00f2 essere abbastanza memoria da gestire questa situazione centinaia o migliaia di volte. Ma alla fine la memoria finir\u00e0. Potrebbe richiedere mesi o anni, o potrebbe non essere mai scoperto.<\/p>\n<p>Le conseguenze potrebbero essere sgradevoli; alla fine l&#8217;ascensore smetterebbe di funzionare. Se il programma avesse bisogno di memoria per aprire le porte, qualcuno potrebbe restare intrappolato all&#8217;interno, visto che non abbiamo risorse per aprirle.<\/p>\n<p>Bisogna notare che il memory leak aumenta finch\u00e9 il programma \u00e8 in esecuzione. Ad esempio, se un calo di elettricit\u00e0 blocca l&#8217;ascensore, al ritorno dell&#8217;alimentazione la memoria sar\u00e0 completamente disponibile ed il lento processo di perdita di memoria deve ricominciare da zero.<\/em>\n<\/p><\/blockquote>\n<p>Questo articolo non pu\u00f2 certo considerarsi esaustivo sull&#8217;argomento, ma speriamo di aver tolto qualche dubbio in pi\u00f9 e di invogliarvi, vista l&#8217;importanza, ad approfondire questo &#8220;apparentemente noioso&#8221; argomento.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Oggi parliamo di gestione della memoria nelle applicazioni iPhone (e iPad). Dopo un primo ottimo articolo sull&#8217;argomento,&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[326,329,330,331,324,332,328,327,325],"class_list":["post-4010","post","type-post","status-publish","format-standard","hentry","category-guide-teoriche","tag-alloc","tag-dealloc","tag-gestione-memoria","tag-gestione-memoria-iphone","tag-init","tag-memory-leak","tag-release","tag-retain","tag-retaincount"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/4010","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/comments?post=4010"}],"version-history":[{"count":14,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/4010\/revisions"}],"predecessor-version":[{"id":4025,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/4010\/revisions\/4025"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=4010"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=4010"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=4010"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}