{"id":5365,"date":"2010-12-17T11:36:02","date_gmt":"2010-12-17T10:36:02","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=5365"},"modified":"2010-12-17T11:42:21","modified_gmt":"2010-12-17T10:42:21","slug":"per-un-pugno-di-kilobytes-gestione-della-memoria","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/per-un-pugno-di-kilobytes-gestione-della-memoria\/","title":{"rendered":"Per un pugno di Kilobytes [Gestione della memoria]"},"content":{"rendered":"<p>L&#8217;utilizzo dell&#8217;oggetto UITabBarController pu\u00f2 sollevarci da grosse problematiche relativamente ad applicazioni con oggetti UIViewControllers multipli e scambio tra le relative viste (UIView). L&#8217;utilizzo indiscriminato di questo oggetto, tuttavia, pone delle serie problematiche circa l&#8217;uso della memoria nel nostro dispositivo. Nel suo utilizzo tipico, infatti, quest&#8217;oggetto prevede l&#8217;assegnazione di un NSArray di oggetti UIViewControllers, gestendone i relativi cambi di vista e, quindi, di memoria. In quest&#8217;articolo, vedremo come rendere l&#8217;oggetto UITabBarController ed i relativi UIViewControllers ad esso legati resistenti alla carenza di memoria, offrendo un paradigma di programmazione che rispecchia quello del &#8220;lazy loading&#8221;.<!--more--><\/p>\n<h4>UITabBarController<\/h4>\n<p>L&#8217;oggetto UITabBarController, permette di gestire pi\u00f9 UIViewControllers con un sistema built-in di scambio tra le viste. Questo significa che tutti i controllori di vista saranno gestiti dal nostro UITabBarController, il quale sar\u00e0 responsabile di spostare il controllo (firstResponder) e la visualizzazione (UIView) ad ognuno degli oggetti UIViewController corrispondenti a seconda della pressione sulla barra di selezione in basso.<br \/>\nOgni oggetto UIViewController, nel suo metodo di istanza di inizializzazione -(id) init, pu\u00f2 personalizzare la sua presenza sulla UITabBar, attraverso un&#8217;icona ed un testo descrittivo. Il tutto \u00e8 davvero molto interessante per una rapida prototipizzazione e realizzazione di un applicativo. A titolo d&#8217;esempio, un tipico utilizzo di UITabBarController, potrebbe essere il seguente:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    \r\n\t\/\/allocazione dell'oggetto UIWindow (programmatically style)\r\n\twindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];\r\n\t\r\n\t\/\/creiamo due istanze di oggetto MainViewController.\r\n\t\/\/ognuno di essi avr\u00e0 una sua icona ed un suo titolo a popolare la TabBar\r\n\tMainViewController *mainVC = [[MainViewController alloc] initWithTitle:@\"titolo1\" \r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  andImage:@\"icon1.png\" \r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tandTag:0];\r\n\tMainViewController *mainVC2 = [[MainViewController alloc] initWithTitle:@\"titolo2\" \r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   andImage:@\"icon2.png\" \r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t andTag:1];\r\n\t\r\n\t\/\/inizializziamo l'oggetto UITabBarController\r\n\tmyTabBarController = [[UITabBarController alloc] init];\r\n\t\r\n\t\/\/creiamo un array autorilasciante con i riferimenti alle due istanze di oggetti MainViewController\r\n\tNSArray *contenitore = [NSArray arrayWithObjects:mainVC, mainVC2, nil];\r\n\t\r\n\t\/\/le loro istanze, adesso, possono essere rilasciate. La responsabilit\u00e0 \u00e8 passata all'array (retaining).\r\n\t[mainVC release];\r\n\t[mainVC2 release];\r\n\t\r\n\t\/\/assegniamo l'array alla property viewControllers del nostro oggetto UITabBarController\r\n\tmyTabBarController.viewControllers = contenitore;\r\n\t\r\n\t\/\/aggiungiamo la property view dell'oggetto UITabBarController alla UIWindow.\r\n\t\/\/in questo modo, verr\u00e0 presentato a schermo il nostro oggetto UITabBarController ed il relativo \r\n\t\/\/UIViewController di default.\r\n\t[window addSubview:myTabBarController.view];\r\n\r\n\r\n    [window makeKeyAndVisible];\r\n    return YES;\r\n}\r\n<\/pre>\n<p>Sino a qui, nulla di nuovo (almeno spero). Il punto da cui partiremo sar\u00e0 proprio questo. Come potete notare, infatti, a questo punto dell&#8217;esecuzione del nostro codice i due oggetti UIViewController (mainVC e mainVC2) sono correttamente allocati ed inizializzati; le loro istanze sono state aggiunte ad un array che, a sua volta, \u00e8 stato assegnato come property all&#8217;oggetto UITabBarController.<br \/>\nA questo punto, il normale utilizzo di UITabBarController, prevede di preoccuparsi solo delle viste e dei relativi controllori. La loro comparsa e scomparsa dallo schermo sar\u00e0 gestita, infatti, dalla barra in basso. Dal punto di vista dell&#8217;impiego della memoria, cosa succede? Da una rapida analisi, capiremo subito che, una volta apparse le relative viste a schermo, per ognuno dei controllori, queste rimarranno sempre presenti in memoria. Poco importa se ci sposteremo tra una view e l&#8217;altra; gli oggetti UIView (e tutti i sotto oggetti ad essi legati) rimarranno in memoria.<\/p>\n<p>Questo comportamento, sebbene a volte potrebbe essere desiderabile, altre volte pone delle serie ipoteche sulle performance ed affidabilit\u00e0 della nostra applicazione.<br \/>\nSupponiamo che la nostra applicazione abbia 4 oggetti UIViewController, ognuno dei quali influisce sulla memoria di sistema con un peso di 4Megabytes. Supponiamo, inoltre, che il mantenere le relative viste in memoria sia assolutamente inutile ai fini del corretto funzionamento della nostra applicazione. L&#8217;utente, infatti, non ne avrebbe nessun giovamento perch\u00e8, quando \u00e8 concentrato su un UIViewController, gli altri cessano di avere importanza.<\/p>\n<h4>L&#8217;approccio lazy loading<\/h4>\n<p>E&#8217; proprio in queste circostanze che entra in gioco, un pattern di programmazione molto interessante che va sotto il nome di &#8220;lazy loading&#8221;. In cosa consiste questa tecnica di programmazione? E&#8217; molto semplice. Si tratta di caricare i nostri oggetti (e quindi le nostre UIView e relativi sotto oggetti) solo quando serve. Da cui il nome &#8220;lazy&#8221;, &#8220;pigro&#8221;. In questo contesto, i nostri oggetti UIViewController, non caricheranno MAI oggetti non indispensabili senza una esplicita richiesta da parte dell&#8217;utente. Cerchiamo di capire meglio cosa vogliamo realizzare.<\/p>\n<p>Quando scriviamo:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n[window addSubview:myTabBarController.view];\r\n<\/pre>\n<p>Scateniamo una serie di messaggi delegati che arrivano al primo UIViewController, inserito nell&#8217;array di cui sopra. In particolare, l&#8217;aggiunta a schermo (addSubview:) di un oggetto UIView, implica il messaggio: -(void)loadView sull&#8217;oggetto target (il nostro UIViewController). Sar\u00e0 quest&#8217;ultimo a creare una vista e mostrarla a schermo:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n\/\/ Implement loadView to create a view hierarchy programmatically, without using a nib.\r\n- (void)loadView {\r\n\tUIView *tmpView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];\r\n\t[tmpView setBackgroundColor:[UIColor redColor]];\r\n\r\n\tmyLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 80)];\r\n\t[myLabel setText:@\"hello, world\"];\r\n\t[myLabel setFont:[UIFont systemFontOfSize:24]];\r\n\t[myLabel setTextAlignment:UITextAlignmentCenter];\r\n\t[myLabel setBackgroundColor:[UIColor clearColor]];\r\n\t[tmpView addSubview:myLabel];\r\n\t[myLabel release];\r\n\t\r\n\tmyField = [[UITextField alloc] initWithFrame:CGRectMake(0, 200, 320, 80)];\r\n\t[myField setBackgroundColor:[UIColor yellowColor]];\r\n\t[tmpView addSubview:myField];\r\n\t[myField release];\r\n\tmyField.delegate = self;\r\n\t\r\n\t\r\n\tUIButton *tmpButton = [UIButton buttonWithType:UIButtonTypeCustom];\r\n\t[tmpButton setImage:[UIImage imageNamed:@\"SantaClaus.jpg\"] forState:UIControlStateNormal];\r\n\t[tmpButton setFrame:CGRectMake(0, 100, 178, 72)];\r\n\t[tmpButton addTarget:self \r\n\t\t\t\t  action:@selector(buttonPressed:) \r\n\t\tforControlEvents:UIControlEventTouchUpInside];\r\n\t\r\n\t[tmpView addSubview:tmpButton];\r\n\t[buttonSet setObject:tmpButton forKey:@\"primo\"];\r\n\tself.view = tmpView;\r\n\t[tmpView release];\r\n}\r\n<\/pre>\n<p>Le ultime due istruzioni, sono fatidiche. E&#8217; a quel punto, infatti, che assegniamo alla property view del controllore, l&#8217;oggetto UIView creato in precedenza che, se rispetta il paradigma di retaining della memoria, sar\u00e0 l&#8217;unico responsabile per tutti gli oggetti ad esso aggiunti (addSubview:). Questo vuol dire che, se eliminiamo l&#8217;oggetto dallo schermo (removeFromSuperview), con esso andranno via anche tutti i sotto oggetti. Proviamo ad estendere questo concetto.<\/p>\n<h4>Un watcher per la memoria<\/h4>\n<p>La classe UIViewController, come tutti gli oggetti che ereditano da NSObject, permette di rispondere ad un metodo molto interessante: -(void)didReceiveMemoryWarning. Questo messaggio viene lanciato dall&#8217;istanza singletone di UIApplication, ogni qualvolta si presenta una situazione di carenza di memoria all&#8217;interno del sistema. Il messaggio viene propagato a tutti gli oggetti dell&#8217;applicazione, cosich\u00e8 ognuno di essi possa prendere iniziativa indipendente rispetto ad un problema di mancanza di memoria.<br \/>\nSar\u00e0 questo metodo a preoccuparsi per noi di &#8220;scaricare&#8221; la memoria, nel caso in cui ne avessimo bisogno durante l&#8217;esecuzione del nostro codice. Seguendo quanto ci siamo detti sopra, ci baster\u00e0 implementare qualcosa di questo tipo:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n- (void)didReceiveMemoryWarning {\r\n\t\/\/ Releases the view if it doesn't have a superview.\r\n    [super didReceiveMemoryWarning];\r\n\t\r\n\tif(!(self.tabBarController.selectedIndex == myTag)) {\r\n\t\tNSLog(@\"remove self.view\");\r\n\t\t[self.view removeFromSuperview];\r\n\t\tself.view = nil;\r\n\t}\r\n}\r\n<\/pre>\n<p>Per ottenere un memory warning &#8220;programmaticamente&#8221;, lanciamo il nostro simulatore e scegliamo l&#8217;opzione &#8220;Simulate Memory Warning&#8221;:<\/p>\n<p><center><br \/>\n<a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/12\/per-un-pugno-di-Kilobytes-Gestione-della-memoria-iPhone-iPad-devapp.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/12\/per-un-pugno-di-Kilobytes-Gestione-della-memoria-iPhone-iPad-devapp.jpg\" alt=\"per-un-pugno-di-Kilobytes-Gestione-della-memoria-iPhone-iPad-devapp\" title=\"per-un-pugno-di-Kilobytes-Gestione-della-memoria-iPhone-iPad-devapp\" width=\"500\" height=\"300\" class=\"aligncenter size-full wp-image-5369\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/12\/per-un-pugno-di-Kilobytes-Gestione-della-memoria-iPhone-iPad-devapp.jpg 500w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/12\/per-un-pugno-di-Kilobytes-Gestione-della-memoria-iPhone-iPad-devapp-300x180.jpg 300w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/12\/per-un-pugno-di-Kilobytes-Gestione-della-memoria-iPhone-iPad-devapp-150x90.jpg 150w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/a><br \/>\n<\/center><\/p>\n<p>Se osserviamo la nostra console, ci accorgeremo che il nostro metodo \u00e8 entrato in azione. Cerchiamo di fare chiarezza su quello che sta accadendo. Ogni oggetto attualmente presente in memoria ricever\u00e0 un messaggio di didReceiveMemoryWarning. A questo punto, verr\u00e0 confrontato il proprio identificatore (myTag) con l&#8217;attuale UIViewController selezionato nella UITabBarController. Se i due indici coincidono, significa che l&#8217;UIViewController in esame \u00e8 proprio quello attualmente mostrato a schermo, quindi non faremo nulla. Tutti gli altri UIViewController, invece, effettueranno una removeFromSuperView del proprio oggetto UIView.<br \/>\nVisto che rimuovere dalla superview, significa anche decrementare il contatore d&#8217;uso dell&#8217;oggetto, questo porter\u00e0 a zero il retainCount degli oggetti UIView non visibili a schermo e, conseguentemente, essi verranno eliminati dalla memoria, lasciando spazio a nuovi oggetti.<\/p>\n<h4>Facciamo diventare pigri i nostri oggetti<\/h4>\n<p>All&#8217;inizio di quest&#8217;articolo vi avevo promesso che avremmo reso &#8220;pigri&#8221;, ovvero capaci di effettuare un lazy-load, i nostri oggetti. In quest&#8217;ultimo paragrafo, cercheremo di usare quanto sopra appreso a nostro vantaggio per l&#8217;obiettivo preposto.<br \/>\nOgni oggetto che eredita da UIViewController, riceve tre messaggi molto importanti:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n-(void)viewDidAppear:(BOOL)animated;\r\n\r\n-(void)viewDidDisappear:(BOOL)animated;\r\n\r\n-(void)viewWillDisappear:(BOOL)animated;\r\n<\/pre>\n<p>Come \u00e8 evidente, la loro implementazione permette di agire nelle fasi di comparsa e scomparsa della property view del controller. Questa \u00e8 un&#8217;ottima notizia per il nostro lavoro. Saranno questi messaggi, infatti, a gestire per noi il caricamento e scaricamento dinamico della memoria. Tutto quello che ci servir\u00e0 fare, sar\u00e0 reimplementare quanto detto sopra all&#8217;interno del metodo -(void)viewDidDisappear:(BOOL)animated:<\/p>\n<pre lang=\"objc\" line=\"1\" escaped=\"true\">\r\n-(void)viewDidDisappear:(BOOL)animated {\r\n\t[self.view removeFromSuperview];\r\n\tself.view = nil;\r\n}\r\n<\/pre>\n<p>A questo punto, ogni qualvolta l&#8217;UIViewController, perder\u00e0 il focus perch\u00e8 l&#8217;utente ha selezionato un&#8217;altra opzione dall&#8217;oggetto UITabBarController in basso, la property view verr\u00e0 rimossa dallo schermo e, con lei, tutti gli oggetti associati avranno un retainCounter pari a zero. Quindi, potranno essere purgati dalla memoria.<\/p>\n<h4>Alcune considerazioni<\/h4>\n<p>Perch\u00e8 tutto funzioni \u00e8 necessario legare agli indici dell&#8217;array i tag dei nostri viewcontrollers, in maniera da essere sempre sincronizzati su dove l&#8217;utente e&#8217; posizionato. Questo viene effettuato durante la fase di inizializzazione dell&#8217;appDelegate.<br \/>\nIl codice sorgente allegato, illustra tutte le caratteristiche di cui sopra con un codice d&#8217;esempio funzionale. A tal proposito, provate ad inserire un testo all&#8217;interno del viewController, premendo sulla barra gialla e scrivendo qualcosa. Il risultato sar\u00e0 la comparsa della nostra frase in alto al centro. Questo vuol dire che abbiamo manipolato le istanze degli oggetti presenti in memoria. Senza simulare un memory warning, provate a spostarvi tra un UIViewController e l&#8217;altro. Interessante vero? Gli oggetti continuano a mantenere lo stato precedente perch\u00e8 sono ancora in memoria. Se, viceversa, effettuiamo una simulazione di memory warning, ci accorgeremo che una volta tornati sul controllore precedente, quest&#8217;ultimo avr\u00e0 perso memoria dello stato e sar\u00e0 stato &#8220;resettato&#8221; al suo aspetto originale. Questo avviene perch\u00e8 esso viene caricato &#8220;dinamicamente&#8221; alla richiesta dell&#8217;utente (pressione sull&#8217;icona in basso dell&#8217;UITabBar). Il controllore, infatti, seguendo quanto specificato nel metodo di didReceiveMemoryWarning, scaricher\u00e0 il suo contenuto ad ogni occorrenza di questo messaggio.<br \/>\nSe invece, implementiamo il metodo di viewDidDisappear, il risultato sar\u00e0 che ad ogni perdita di focus del controllore, il suo contenuto verr\u00e0 eliminato dalla memoria. Non avremo piu&#8217;, quindi, quella sorta di memoria di stato tra un viewcontroller e l&#8217;altro.<\/p>\n<p>La morale della favola \u00e8: fate molta attenzione alla memoria. Averne in abbondanza non ci autorizza a sprecarla. Un occhio di riguardo ai vecchi dispositivi (iPhone 2G\/3G), non potr\u00e0 che aiutare tutti: utenti, ambiente, le nostre tasche. <\/p>\n<p>Buon natale e\u2026buona programmazione!<\/p>\n<p>Costantino Pistagna<\/p>\n<p style=\"text-align: center;\"><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/12\/ClassEx_3.zip\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/05\/download_icon.png\" alt=\"\" width=\"33\" height=\"40\" align=\"middle\" \/><\/a> Se avete problemi con il progetto, <a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/12\/ClassEx_3.zip\">questo \u00e8 il nostro file di esempio.<\/a><\/p>\n<p><center><br \/>\n<a href=\"http:\/\/clk.tradedoubler.com\/click?p=24373&amp;a=1735897&amp;g=0&amp;url=http:\/\/itunes.apple.com\/it\/app\/allertasoglie-tre\/id393552998?mt=8&amp;partnerId=2003\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/11\/bn-Allerta-Soglie-per-tre-iphone-devapp.jpg\" alt=\"bn-Allerta-Soglie-per-tre-iphone-devapp\" title=\"bn-Allerta-Soglie-per-tre-iphone-devapp\" width=\"493\" height=\"113\" class=\"aligncenter size-full wp-image-5215\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/11\/bn-Allerta-Soglie-per-tre-iphone-devapp.jpg 493w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/11\/bn-Allerta-Soglie-per-tre-iphone-devapp-300x68.jpg 300w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2010\/11\/bn-Allerta-Soglie-per-tre-iphone-devapp-150x34.jpg 150w\" sizes=\"auto, (max-width: 493px) 100vw, 493px\" \/><\/a><br \/>\n<\/center><\/p>\n","protected":false},"excerpt":{"rendered":"<p>L&#8217;utilizzo dell&#8217;oggetto UITabBarController pu\u00f2 sollevarci da grosse problematiche relativamente ad applicazioni con oggetti UIViewControllers multipli e scambio&#8230;<\/p>\n","protected":false},"author":476,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[8],"tags":[479,527,331,524,525,526],"class_list":["post-5365","post","type-post","status-publish","format-standard","hentry","category-guide-varie","tag-costantino-pistagna","tag-gestione-memoria-ipad","tag-gestione-memoria-iphone","tag-lazy-loading","tag-uitabbarcontroller","tag-uiviewcontrollers"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/5365","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\/476"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/comments?post=5365"}],"version-history":[{"count":11,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/5365\/revisions"}],"predecessor-version":[{"id":5378,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/5365\/revisions\/5378"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=5365"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=5365"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=5365"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}