86746175 iPhone Programming

78
Approfondimenti tematici Il corso completo per imparare facilmente a programmare lo smartphone Apple iPhone PROGRAMMING

Transcript of 86746175 iPhone Programming

Page 1: 86746175 iPhone Programming

Approfondimenti tematici

Il corso completo per imparare facilmente a programmare

lo smartphone Apple

iPhonePROGRAMMING

Page 2: 86746175 iPhone Programming

Questo approfondimento tematico è pensato per chi vuol imparare a programmare e creare software per l’Apple iPhone.La prima parte del testo guida il lettore alla cono-scenza degli strumenti necessari per sviluppare sulla piattaforma mobile di Cupertino.Le sezioni successive sono pensate per un apprendi-mento pratico basato su esempi di progetto: la crea-zione di un browser su misura, la gestione dell’inter-faccia, la programmazione di un’agenda e di una to do list, la gestione corretta di celle e tabelle, l’utilizzo dell’accelerometro, la progettazione di un RSS reader e via dicendo.Una serie di esempi pratici da seguire passo passo che – creando applicazioni testabili e perfettamente funzionanti - spingono il lettore a sperimentare sul campo il proprio livello di apprendimento e lo invita-no a imparare divertendosi.

iPhone programming

Page 3: 86746175 iPhone Programming

CREARE SOFTWAREPER L’APPLE IPHONE . . . . . . . . . . . . . .4 Inizia da questo numero un corso dedicato allo sviluppo dell’apple iphone, lo smartphone che ha rivoluzionato il mondo mobile. in questo primo appuntamento faremo la conoscenza di tutti gli strumenti necessari

APPLE IPHONEPROGRAMMING . . . . . . . . . . . . . . . . .11Continuiamo il corso inerente la programmazione del dispositivo smartphone apple iphone, illustrando gli strumenti necessari per completare l’applicazione mini browser avviata nel numero precedente della rivista

LA NOSTRA APPÈ SULL’APPLE STORE (2) . . . . . . . . . .11pubblicare un’applicazione su apple store adoperando i portali iphone provisioning portal e itunes connect: scopriamo come farlo al meglio,seguendo tutto il percorso che arriva fino alla pubbli-cazione

CREARE SOFTWAREPER L’APPLE IPHONE . . . . . . . . . . . . .17In questa terza lezione modifichiamo il minibrowser introdotto nel precedente articolo della serie, intro-ducendo altri concetti fondamentali come la gestio-ne degli aspetti grafici e l’utilizzo del componente uiwebview

REALIZZIAMO UNAAGENDA PER IPHONE . . . . . . . . . . . .23 Da questo numero inizia una trattazione che è un po’ il cuore di quasi ogni applicazione iphone. stiamo parlando delle tabelle. vedremo come si creano, quali tipologie utilizzare a seconda del contesto, e come gestirle al meglio

IPHONE: GESTIONEDELLA MEMORIA . . . . . . . . . . . . . . . .29Per chi si accinge a sviluppare applicazioni per lo smar-tphone di casa apple, uno degli argomenti sicuramente più ostici, è la gestione della memoria. In questo artico-lo affronteremo proprio questa importante tematica

LEAK E ZOMBIEIN AGGUATO . . . . . . . . . . . . . . . . . . . . .35In questo articolo approfondiremo ulteriormente i concetti legati alla gestione della memoria, in modo particolare faremo la conoscenza di quelliche in gergo vengono chiamati zombie e leaks

COME POPOLAREUNA UITABLEVIEW . . . . . . . . . . . . . .20In questo articolo popoliamo la nostra tabella con un elenco di voci ottenute interrogando una serie di strut-ture dati. nella fattispecie vedremocome inserire delle semplici righe, creare delle sezioni e navigare tra le stesse

GESTIONE DELLECELLE NELLE TABELLE . . . . . . . . . . . .47 In questo articolo trattiamo delle varie funzionalità offerte dall’sDk per inserire e cancellare una o più righe nelle tabelle: elemento fondamentale nella costruzione delle interfacce, sia per l’input che per la visualizzazio-ne dei dati

UNA SVEGLIADIGITALE PER IPHONE . . . . . . . . . . . .53In questo articolo mostriamo come realizzare una sveglia digitale che ci avviserà di impegni e scadenze imminenti. sarà l’occasione di approfondire i concetti legati alla gestione dell’interfaccia e del timer

UN ACCELEROMETROPER AMICO . . . . . . . . . . . . . . . . . . . . .59L’accelerometro presente nell’iphone è uno dei com-ponenti più efficaci nel consentire all’utente un’intera-zione con giochi e applicativi maggiormente semplice e intuitiva. vediamo come integrarlo nelle nostre appli-cazioni

IPHONE: GESTIREIL MULTI-TOUCH . . . . . . . . . . . . . . . .63Lo schermo multitouch consente di interagire con un dispositivo senza la necessità di fornire ingombranti tastiere. impariamo a intercettare le azioni degli utenti e a gestirle in modo da non far rimpiangere tastiera e mouse

UN RSS READER SU IPHONE . . . . . . .68Distribuire informazioni attraverso le nostre applicazioni può essere molto più semplice adoperando la classe nsxmlparser. saremo così in grado di raccogliere e mostrare i contenuti degli rss consumando pochissima banda

UN RSS READER PER IPHONE . . . . . .73Adoperando la classe nsxmlparser, possiamo intera-gire con i contenuti di un feed rss. in questo articolo mostreremo come fare e come implementare una strut-tura dati per la visualizzazione delle info ricevute

Page 4: 86746175 iPhone Programming

iPhone programming 4

iPhone programming Imparare a programmare l’Apple iPhone

CREARE SOFTWAREPER L’APPLE IPHONEINIZIA DA QUESTO NUMERO UN CORSO DEDICATO ALLO SVILUPPO DELL’APPLE IPHONE, LO SMARTPHONE CHE HA RIVOLUZIONATO IL MONDO MOBILE. IN QUESTO PRIMOAPPUNTAMENTO FAREMO LA CONOSCENZA DI TUTTI GLI STRUMENTI NECESSARI

Acausa della NDA (Non DisclosureAgreement) che nel mese di luglio delloscrso anno ha accompagnato la distribu-

zione dell'SDK, parallelamente all'uscita in Italiadell'iPhone, non ci è stato possibile pubblicare isuccessivi articoli pianificati per il seguentecorso di programmazione di questo interessantedispositivo. Negli ultimi mesi sono state apporta-te numerose migliorie al firmware (terminatecon il rilascio della versione stabile 2.2.1), erecentemente l'OS 3.0 con il relativo SDK 3.0, (infase di beta 3 al momento della scrittura di que-sto articolo), fornirà ancora più strumenti per iprogrammatori che desiderano utilizzare almeglio le funzionalità di questa piattaforma disviluppo. Molti concetti fondamentali, tra cuiarchitettura dell'iPhone, su come è strutturato ilframework, la disposizione delle cartelle dei pro-getti creati con Xcode, inclusi numerosi consiglidi ottimizzazione, sono stati già affrontati nelnumero 130 di questa rivista. Consigliamo a que-sto proposito di ridare una lettura a tali pagine,perché non verranno trattati, se non quando saràstrettamente necessario. Ricordiamo, inoltre, cheè possibile realizzare un software completamen-te funzionante senza avere acquistato alcuniPhone o iPod Touch di seconda generazione, esenza acquistare la licenza. Di contro, si devonoaccettare le seguenti limitazioni: in alcune situa-zioni tale software, eseguito all'interno dell'emu-latore, non si comporterà in maniera fedele alvero dispositivo, quello fisico, rendendo addirit-tura inutilizzabile il vostro prodotto. Questo èdovuto sia al fatto che l'emulatore suddetto vieneeseguito utilizzando le risorse hardware (netta-mente più prestanti sotto ogni aspetto) delvostro computer, sia perché manca di alcunefunzionalità, come il GPS e l'accelerometro adesempio, sia per alcune discrepanze che moltiprogrammatori hanno riscontrato nei vari test;se non acquisterete una licenza non potretetestarlo su alcun iPhone/iPod e non potrete ven-derlo sull'Apple Store. La scelta se utilizzare un

iPhone o un iPod Touch spetta a voi, un iPodTouch di seconda generazione è comunque vali-do, poiché ha un hardware leggermente più pre-stante rispetto al “fratello”, manca però delle fun-zionalità GPS, UMTS e fotocamera; come dettoprecedentemente lo sblocco del Bluetooth con ilfirmware 3.0 nell'iPod Touch ha eliminato questaulteriore differenza tra i due dispositivi.

REGISTRAZIONE E SDKPer iniziare a programmare è necessario effettua-re due operazioni: la prima consiste nel registrar-si come sviluppatore, divenire quindi RegisterediPhone Developer, operazione completamentegratuita. Basterà accedere al seguente indirizzohttp://developer.apple.com/iphone/

cliccare poi sulla voce in alto, register, e deciderese creare un nuovo account, oppure, nel caso visiate già registrati utilizzando mobileMe, l'itunesStore, l'ADC (Apple Developer Connection) o AppleOnline Store, utilizzare il vostro identificativo cor-rente. A questo punto è necessario inserire tuttele informazioni anagrafiche, accettare

MOBILE � Imparare a programmare l’Apple iPhone

ht tp ://www. ioprogrammo. i t

G 30 /Giugno 2009

Conoscenze richiesteOOP

SoftwareMac OS X 10.5 osuperiore

Impegno

¥Tempo di realizzazione

REQUISITI

Fig. 1: La home page del sottosito dedicato allo svilup-po di applicazioni per iPhone

030-036:088-093-corsi-xsl 27-04-2009 18:05 Pagina 30

Page 5: 86746175 iPhone Programming

5 iPhone programming

iPhone programmingImparare a programmare l’Apple iPhone

ht tp ://www. ioprogrammo. i t Giugno 2009/ 31 G

Imparare a programmare l’Apple iPhone � MOBILE

l'informativa e, alla ricezione dell'email di con-ferma, seguire il link che vi verrà inviato: sietecosì divenuti Registered iPhone Developer.La seconda, ed ultima, operazione da effettuare,consiste nello scaricare e installare sul vostroMac l'SDK; la versione stabile, 2.2.1, consiste inun file di circa 1.7GB, consigliamo quindi di uti-lizzare una connessione relativamente veloce; èinoltre necessario avere almeno Leopard aggior-nato alla versione 10.5.4 e disporre di sufficientespazio sul proprio disco, poiché, inclusa la docu-mentazione, verranno occupati oltre 5GB.All'avvio dell'installer sarà richiesto quali com-ponenti installare: quelli di default sono suffi-cienti. Al termine di questo non breve processo lelibrerie, tutto il software necessario, tra cui ilsimulatore, Xcode, l'editor per i progetti,Interface Builder, l'editor WYSIWYG, Shark eInstruments, necessari per il tuning delle vostreapplicazioni, compresa una versione parzialedella documentazione saranno contenuti all'in-terno della cartella Developer. All'interno dellacartella Applications trovano posto quegli stru-menti che adopererete in ogni progetto: XCode,Interface Builder, Shark (sottocartella Performance Tools) e Instruments; poiché da Xcode èpossibile richiamare tutti gli altri applicativi,basterà trascinare solo tale software nel Dock peravere disponibile tutti questi programmi con uncolpo di clic. La documentazione non viene

installata completamente, e si ha libera scelta seconsultarla richiamando le pagine disponibilionline, prelevate automaticamente quandonecessario, risparmiando spazio sul proprio HD,o scaricarla localmente; in questo ultimo caso,sarà necessario avviare XCode, selezionare ilmenu Help->Documentation e cliccare sui singolitasti get che si trovano vicino alle singole catego-rie; sono inoltre disponibili documenti riguar-danti la libreria WebObjects (bisogna selezionarlatra i componenti aggiuntivi durante la fase diinstallazione) e le API Java 1.4 e 1.5, questo per-ché la versione di Xcode (e degli altri softwareinstallati) consente anche di realizzare applicati-vi, plugin, moduli aggiuntivi per Mac OS in Java,C++, Ruby e altri linguaggi. La versione 2.2.1 nonè dotata di documentazione, poiché è stata rila-sciata per correggere alcuni bug, non è quindiavvenuta alcuna modifica all'API, per tale motivola documentazione più aggiornata corrisponde aquella fornita con la 2.2. Utilizzando il campo ditesto presente in alto a destra, si ricercherannoquelle funzioni e/o metodi disponibili per unaconsultazione; è possibile inoltre filtrare su qualeAPI effettuare la ricerca.

INIZIAMO L’AVVENTURAIn questa prima puntata creeremo un browserweb minimale che ci permetterà di conoscere iconcetti fondamentali di questa tecnologia, pro-gressivamente presenteremo le nozioni necessa-rie per completare il progetto e renderlo piena-mente operativo; non creeremo nuove classi perevitare confusione, ma utilizzeremo quelle for-nite quando si crea un nuovo software usando ilwizard; ricordiamo che la struttura delle classicon cui lavoreremo è organizzata con una strut-tura ad albero (Fig.2), avente una radice(NSObject al più alto livello, UIWindow scenden-do di qualche gradino) a cui si associano i vari

Fig. 3: La struttura della cartella di installazionedell'SDK

Fig. 2: La fase di registrazione, in cui sono richiestetutte le vostre informazioni anagrafiche

NOTA

RIFERIMENTI WEBPer la creazionedell'account e perscaricare l'SDK, visitate ilseguente percorso web:http://developer.apple.com/iphone/

All’indirizzo che seguetrovate una descrizionedettagliata delle novitàfornite con il nuovo SDK3.0: http://developer.apple.com/iphone/prerelease/library/releasenotes/General/WhatsNewIniPhoneOS/Articles/iPhoneOSv3.html

030-036:088-093-corsi-xsl 27-04-2009 18:05 Pagina 31

Page 6: 86746175 iPhone Programming

iPhone programming 6

iPhone programming Imparare a programmare l’Apple iPhoneMOBILE � Imparare a programmare l’Apple iPhone

ht tp ://www. ioprogrammo. i t

G 32 /Giugno 2009

UIView Controller (contenitori invisibili UIVIEW)e i cui figli sono discendenti della classe UIView(bottoni, testi e altri componenti). L'approccio migliore per iniziare a programmarel'iPhone/iPod Touch è quello top-down, adope-rando quei componenti dell'interfaccia graficapredefiniti, disponibili attraverso l'InterfaceBuilder (IB), aggiungendo, quando necessario, ilcodice minimale richiesto per ottenere il risulta-to desiderato. Successivamente si potrà, nel casolo si trovasse limitante oppure per semplici gustipersonali, abbandonare completamente talestrumento visuale, e realizzare tutto in puroObjective-C. Riprendiamo brevemente il discor-so, iniziato nell'articolo di presentazionesull'iPhone, pubblicato a settembre e riguardan-te il design pattern MVC; dovrete metabolizzaretale concetto per non riscontrare problemi nellafase di sviluppo del vostro primo progetto. MVCè acronimo di Model View Controller, indica i trecomponenti utilizzati per realizzare applicazioniin grado di adattarsi meglio a variazioni dellapropria struttura interna; con Model si indicala/le struttura/e dati utilizzata/e per gestire leinformazioni necessarie per un corretto funzio-namento del vostro software, possono esserevariabili, array, strutture più complesse, file loca-li o remoti e anche database; con View (che nona caso viene identificata con la classe UIView) siindica l'interfaccia grafica necessaria per con-sentire all'utente finale una consultazione e/o

interazione con tali dati; lo strumento per realiz-zarle è Interface Builder (utilizzando i file xib)oppure puro codice Objectice-C (creando tutte lestrutture grafiche all'interno di file .m/.h); conController intendiamo quella parte di codicenecessaria per una corretta comunicazione tra ledue strutture precedenti: è, di fatto, la compo-nente in cui risiede “l'intelligenza” del vostrosoftware (e dove ovviamente riscontrerete laquasi totalità dei problemi e degli errori), provve-dendo a creare un mapping tra cosa devonomostrate i vari componenti grafici e in che mododevono essere modificati conseguentemente aun'interazione da parte dell'utilizzatore delvostro software; in questo contesto il Controller(anche qui non a caso identificato con la classeUIViewController) corrisponde al codice presen-te nei file .m/.h, realizzati con il linguaggioObjective-C (è possibile comunque inserire codi-ce C e C++ nel caso fosse necessario velocizzarealcune operazioni o per utilizzare le API a livellopiù basso).

CREAZIONE DELPROGETTO E XCODEIl progetto Xcode fornito dal Wizard, che ci con-sente di realizzare il nostro browser nella manie-ra più veloce, utilizzando un'istanza di unUIViewController contenente una della classeUIView si chiama View-Based Application.Decidiamo il nome dell'applicativo, e posizionia-molo in una cartella a nostra scelta. Al terminedella creazione ci verrà presentata la schermatadi Fig.7. Analizziamola in breve: è suddivisa prin-cipalmente in tre aree: nella parte alta troviamo icomandi necessari compilare il nostro software,selezionare la piattaforma di testing, simulatoreo dispositivo fisico (nel caso abbiate già acqui-stato la licenza), nella colonna di sinistra trovia-

Fig. 4: La struttura ad albero dell'API

Fig. 5: Una generalizzazione del design pattern MVCutilizzato

NOTA

DESIGN PATTERNMVC

Model-View-Controller(MVC, talvolta tradotto in

italiano Modello-Vista-Controllore) è un pattern

architetturale molto diffusonello sviluppo di interfacce

grafiche di sistemisoftware object-oriented.

Viene sovente utilizzato daframework basati su Ruby,Java (Swing, JSF e Struts),

su Objective C o su .NET.Il pattern è basato sulla

separazione dei compiti frai componenti software che

interpretano tre ruoliprincipali:

- il model fornisce imetodi per accedere ai dati

utili all'applicazione;

- il view visualizza i daticontenuti nel model e si

occupa dell'interazione conutenti e agenti;

- il controller riceve icomandi dell'utente (in

genere attraverso il view) eli attua modificando lo

stato degli altri duecomponenti

030-036:088-093-corsi-xsl 27-04-2009 18:05 Pagina 32

Page 7: 86746175 iPhone Programming

7 iPhone programming

iPhone programmingImparare a programmare l’Apple iPhone

ht tp ://www. ioprogrammo. i t Giugno 2009/ 33 G

Imparare a programmare l’Apple iPhone � MOBILE

mo tutti i file del progetto, comprese le librerieutilizzate, in quella destra viene visualizzato ilcontenuto del file selezionato a sinistra. Per aggiungere un file al nostro progetto è suffi-ciente crearlo utilizzando il menu File->NuovoFile, oppure ne trasciniamo uno preesistente all'in-terno della colonna di sinistra, prelevandolo da unafinestra di Finder; è possibile utilizzare anche ilcomando Add to Project presente all'interno delmenu Project, presenterà una finestra da cui sele-zionare uno o più file presenti nel proprio compu-ter: questa operazione è necessaria quando si crea-no risorse con software diversi come audio, video oimmagini. Copiare tramite Finder un file all'inter-no della cartella del nostro progetto non è suffi-ciente, poiché XCode richiede che i file che dovràcompilare vengano aggiunti utilizzando una dellemodalità suddette: è quindi possibile inserire qua-lunque tipo di file nella cartella del progetto eaggiungere solo un sottoinsieme di tali elementi inXCode. Dopo aver selezionato il file ci verrà presen-tata una finestra in cui potremo selezionare ilcheckbox corrispondente alla voce Copy items intodestination group's folder (if needed), tale operazio-ne non è obbligatoria, ma in caso non la si attivas-se non si copierebbe localmente, all'interno dellacartella del progetto e si lavorerebbe con un linksimbolico; una cancellazione o uno spostamentodel file originale (quello fisico) dalla sua posizione(è situato in una diversa cartella del proprio com-puter) non permetterebbe di compilare il progetto;selezionando quindi tale opzione, abbiamo lagaranzia di avere all'interno della cartella del pro-getto tutti i file necessari, rendendo estremamenteveloce qualunque operazione di backup o invio adaltri sviluppatori. Analizziamo ora la finestra disinistra. Noterete alcune cartelle, di colore giallo,queste non esistono fisicamente nel vostro compu-ter, si chiamano Group(s) e hanno lo scopo di con-sentire un raggruppamento visivo delle proprierisorse, sono delle cartelle virtuali, è possibile quin-di crearle liberamente utilizzando il tasto destro delmouse (Add->New Group) o dal menu Project.

La cartella frameworks contiene le librerie che sidesidera inserire nel progetto, le tre fondamentali,inserite automaticamente, sono UIKit.framework,Fondation.framework e Core Graphics.framework;per aggiungerne altre basterà trascinarle all'internodel progetto, oppure utilizzare il destro del mouse eselezionare Add Existing Frameworks. Le altre car-telle virtuali, rappresentate con differenti icone,svolgono principalmente funzione di raggruppa-mento automatico di diversi tipi di informazioni,errori di programmazione, file grafici xib, book-mark, ad esempio. Premendo CTRL + INVIO, oppu-re selezionando l'icona Build & Go, o dal menuBuild la voce Build & Run avvieremo il simulatore.

I FILE XIB E L’INTERFACEBUILDEROra che abbiamo creato un progetto, iniziamo adescrivere i due componenti fondamentali chesono onniprensenti nei progetti per iPhone, inmaniera diretta e/o indiretta (utilizzando compo-nenti che da questi derivano): UIViewController eUIView. Ogni istanza di UIViewController è invisi-bile, e il suo scopo è quello di contenitore delleUIView (a cui appartengono tutti i componentivisuali, bottoni, testi inclusi) che in esso andremo ainserire, può svolgere anche ruolo di gestore deglieventi, rafforzando ulteriormente il concetto dicome nell'MVC il Controller sia “trasparente”, nonabbia quindi un aspetto grafico, ma sia puramentecodice. Dopo avere creato il progetto troveremodue file xib all'interno della colonna di sinistra diXCode, un primo chiamato MainWindow.xib, ilquale contiene la UIWindow, la root del nostrosoftware, che provvede automaticamente a conte-nere e gestire tutte le sottofinestre (e la catena dieventi), nel nostro caso sono un viewcontrollercontenente una view, e un secondo file il cui nome,terminante per ViewController, dipenderà da quel-

Fig. 6: La finestra di scelta del progetto

Fig. 7: L'interfaccia di sviluppo proposta dal tool XCode

NOTA

INTERFACE BUILDERInterface Builder èun'applicazione facenteparte di Xcode. Consenteagli sviluppatori che usanoCarbon e Cocoa didisegnare interfaccegrafiche per le applicazioniusando uno strumentografico, senza la neccessitàscrivere nessuna riga dicodice. L'interfacciarisultante è salvata in unfile .nib (abbreviazione diNeXT Interface Builder).

030-036:088-093-corsi-xsl 27-04-2009 18:05 Pagina 33

Page 8: 86746175 iPhone Programming

iPhone programming 8

iPhone programming Imparare a programmare l’Apple iPhone

lo del nostro progetto, nel nostro caso è ioProgrammoArt2Viewcontroller.xib; quest'ultimo file èquello che ci interessa e che andremo a modificarevisivamente con Interface Builder. La nostra UIView, al cui interno andremo a posizio-nare alcuni componenti visuali (pulsanti, testi,immagini), ha necessità di essere ospitata in unUIViewController (o classe derivata) per esseremostrata su schermo, in questo caso tale operazio-ne è stata gestita automaticamente dal wizard, main altri casi è necessario creare tramite IB tale rela-zione, oppure adoperando Objective-C. In un pro-getto sviluppato con XCode è possibile creare unnumero indeterminato di file xib, realizzati adope-rando Interface Builder, l'editor WYSIWYG disponi-bile tra i software forniti nell'SDK; ogni file conestensione xib è in realtà un semplice XML (per chiavesse utilizzato Adobe Flex è un approccio similea quello dei file MXML, mentre per chi conosceWindows Presentation Foundation è simile, sem-pre concettualmente parlando, a quello ideato peri file XAML) che viene letto, interpretato e mostratoin maniera visuale da IB. Diamo uno sguardo velo-ce alla struttura di uno di questi file, premendo ildestro del mouse selezioniamo la voce “Open asSouce Code File”, otterremo il seguente codice (quiridotto per la sua estrema lunghezza):

<?xml version="1.0" encoding="UTF-8"?>

<archive type="com.apple.InterfaceBuilder3.

CocoaTouch.XIB" version="7.02">

<data>

<int key="IBDocument.SystemTarget">528</int>

<string

key="IBDocument.SystemVersion">9E17</string>

<string key="IBDocument.InterfaceBuilder

Version">672</string>

<string

key="IBDocument.AppKitVersion">949.33</string>

<object class="IBUIView" id="774585933">

...

<string key="NSFrameSize">{320, 460}</string>

<object class="NSColor" key="IBUIBackgroundColor">

..

<int key="NSColorSpace">1</int>

<bytes key="NSRGB">MC44MDQzNDc4

MSAwLjM5MzQ0OTI4IDAuMjk4MDAzOTcAA</bytes>

</object>

...

<reference key="object" ref="774585933"/>

<reference key="parent" ref="360949347"/>

<string key="objectName">ViewPrincipale</string>

….

</data>

</archive>

Un file XML relativamente complesso, ma bastafare qualche test all'interno di IB per verificare inche modo questa struttura viene modificata; senotate, la riga <object class="IBUIView" id="774585933"> contiene al suo interno molte delleinformazioni riguardanti la view che è stata creataautomaticamente, ad esempio le dimensioni, ilcolore di sfondo e quali componenti vengonosimulati visivamente (spiegheremo più in avantitale funzionalità); il numero ID viene usato in altrelocazioni di tale file per riferimenti a questo stessooggetto (come avviene in una delle ultime righe<reference key="object" ref="774585933"/.Modificare direttamente tale file è inutile, poiché IBserve proprio a questo, inoltre si rischia di renderloinutilizzabile, ma sapere almeno che è un formatoleggibile da qualunque utente e non è codifica-to/compilato, può risultare utile in alcuni casi,

MOBILE � Imparare a programmare l’Apple iPhone

ht tp ://www. ioprogrammo. i t

G 34 /Giugno 2009

Fig. 8: L'interfaccia di Interface Builder, il wizard che consente di “disegnare” la nostra applicazione

NOTA

APPLE SDK IPHONE 3.0

iPhone OS 3.0, la cuirelease ufficiale dovrebbeessere rilasciata in questi

giorni, include un SoftwareDeveloper Kit (SDK)

aggiornato con oltre 1000nuove Application

Programming Interface(API) tra cui In-App

Purchases, connessioniPeer-to-Peer, una

interfaccia applicativa pergli accessori, accesso allalibreria musicale dell’iPod,

un nuovo Map API e lenotifiche Push. Saranno

disponibili oltre 100 nuovefunzioni per gli utilizzatoridi iPhone e iPod touch, tra

cui il Taglia, Copia e Incolla(che potranno essere

utilizzate all’interno e tra leapplicazioni), gli MMS

(disponibili solo su iPhone3G) per spedire e ricevere

immagini, contatti, fileaudio e posizioni

geografiche con la funzioneMessaggi, il Bluetooth

stereo, la sincronizzazionedelle note con Mac e PC;

shake per attivare lafunzione Shuffle, il

controllo parentale per iprogrammi televisivi, i film

e le applicazioni dell’AppStore e il login automatico

agli hot spot Wi-Fi.

030-036:088-093-corsi-xsl 27-04-2009 18:05 Pagina 34

Page 9: 86746175 iPhone Programming

9 iPhone programming

iPhone programmingImparare a programmare l’Apple iPhone

ht tp ://www. ioprogrammo. i t

come quando si corrompe per qualche motivo(crash di IB, del sistema operativo, o della locazio-ne fisica in cui viene memorizzato) e non si riescepiù a modificare una (o più) proprietà di un (o più)componente da IB. Questo formato viene automaticamente convertitoin fase di compilazione in maniera trasparente.Cliccando invece due volte sul tale file avvieremoautomaticamente IB.Partendo da sinistra e andando verso destra abbia-mo: la finestra di anteprima, la Document Windowe l'Inspector; la prima è responsabile della visualiz-zazione dell'anteprima dell'interfaccia grafica,mostrerà quindi tutti i componenti grafici che inse-riremo con una semplice operazione di trascina-mento dalla finestra chiamata Library. La secondafinestra che incontriamo, la Document Window,mostra un elenco di tutti gli oggetti presenti nel file.xib, sia grafici che non, ci permette, inoltre, di sele-zionarli e poter così visualizzare i dettagli all'inter-no della finestra alla sua destra, l'Inspector; la chiu-sura della Document Window comporta la chiusu-ra di tutto il file xib in IB, rendendo quindi necessa-rio riaprire tale file dal menu File->Open o facendonuovamente doppio clic in XCode. I tre componen-ti visibili nel Document Window inizialmente sonoil file owner, il cui scopo è quello di identificarequale classe (la coppia .m/.h) è collegata al file .xibche stiamo usando (sotto la colonna type risultaioProgrammoArt2ViewController, creata automati-camente dal wizard), first responderer utilizzato pergestire la catena di eventi, e la nostra View, che inquesto caso non ha alcuna coppia di file .m/.h asso-ciata (basta notare che sotto la colonna Type risultaUIView invece di un nome di classe creato manual-mente dal sottoscritto). Le quattro sottofinestredell'Inspector forniscono tutte le informazioniriguardanti un determinato componente selezio-nato. Cliccando sulla view, utilizzando il DocumentWindow abbiamo ottenuto i seguenti risultati.Attributes Inspector: nella parte alta troviamo lasezione chiamata view simulated bar metrics, chepermette di aggiungere solo visivamente alcunicomponenti grafici, tipici di classi derivate daUIViewController più complesse, come il Tab BarController e il Navigation Controller, in modo dafornire un feeback visivo di come tale view cam-bierà le proprie proporzioni all'interno dei conte-nitori; l’anteprima non modifica in alcun modo lastruttura effettiva della view, è un semplice stru-mento di valutazione. Nella parte bassa sono pre-senti opzioni come la trasparenza, il colore dellaview, come questa si adatta in funzione delledimensioni settate, se consente l'interazione conl'utente, e altre funzioni specifiche a seconda dellasottoclasse di UIView utilizzata (ad esempio, unUIButton, un semplice bottone, ha proprietà diver-se di un UILabel, il cui scopo è solo di visualizzare

del testo); spiegheremo in dettaglio tutti questicampi nei prossimi articoli a seconda delle esigen-ze. Nel Connection Inspector avremo modo divisualizzare i collegamenti tra view e controller (manon solo). Nella Size Inspector troviamo i tipici con-trolli di dimensionamento e allineamento.Nell'Identity Inspector trovano posto tutte le infor-mazioni riguardanti relazioni ed eventi gestiti dallaview, compresa la classe che la gestisce (si parleràdi IBOutlet e IBAction). Come spiegato precedentemente, IB genera fileche appartengono al componente del pattern MVCchiamato View, non c'è modo diretto di scrivereall'interno di questo editor codice Objective-C, pertale motivo dobbiamo creare una coppia di file perogni componente grafico con il quale vogliamo chel'utente interagisca (sia derivato daUIViewController che da UiView), o che desideria-mo animare o trasformare in qualche modo, eassociare in Interface Builder questa coppia di file

Giugno 2009/ 35 G

Imparare a programmare l’Apple iPhone � MOBILE

Fig. 10: L’Attributes Inspector dell’Interface Builder

Fig. 9: Come si identificano i componenti dell'MVC utilizzando un file XIB

NOTA

ALTERNATIVE AMAC OSX E XCODEIBM ha rilasciato unapropria guida allo sviluppodi applicazioni per iPhone,utilizzando come sistemaoperativo Windows o Linux.La guida si intitola Writenative iPhone applicationsusing Eclipse CDT. Si trattadi un PDF in lingua ingleseche è possibile scaricaregratuitamente da questoindirizzo web:http://www.ibm.com/developerworks/edu/os-dw-os-eclipse-iphone-cdt.html. Per lo sviluppo vengonoutilizzati Eclipse e Cygwin,tuttavia, le applicazionisviluppate secondo questoschema non possonoessere pubblicatesull'AppStore, ma soltantosu sistemi come Cydia eInstaller.

030-036:088-093-corsi-xsl 29-04-2009 10:38 Pagina 35

Page 10: 86746175 iPhone Programming

iPhone programming 10

iPhone programming Imparare a programmare l’Apple iPhone

al componente visivo: i file in questione avrannoestensione .h e .m, e rappresenteranno quindi ilController del nostro componente (in questo pro-getto non diamo attenzione al Model). Questi duefile sono complementari, e rappresentanoun'unica classe realizzata con un linguaggio objectoriented (categoria alla quale ovviamentel'Objective-C appartiene); con l'estensione .h siindica l'interfaccia della classe, rappresenta quindiquell'insieme di metodi e variabili che si desiderarendere disponibili esternamente, alle altre classi ea Interface Builder. Ecco come si presenta l'interfaccia del viewcon-troller, il file .h, realizzato automaticamente quan-do si crea un nuovo progetto View-BasedApplication:

#import <UIKit/UIKit.h>

@interface ioProgrammoArt2ViewController :

UIViewController {}

@end

Mentre quello che segue è il file .m, sempre creatoautomaticamente quando si crea il nuovo progetto,sarà:

#import "ioProgrammoArt2ViewController.h"

@implementation ioProgrammoArt2ViewController

/*

- (id)initWithNibName:(NSString *)nibNameOrNil

bundle:(NSBundle *)nibBundleOrNil {...}

- (void)loadView {}

- (void)viewDidLoad {... }

*/ /*

-(BOOL)shouldAutorotateToInterfaceOrientation:

(UIInterfaceOrientation)interfaceOrientation {

return (interfaceOrientation ==

UIInterfaceOrientationPortrait); }

*/

- (void)didReceiveMemoryWarning {

[super didReceiveMemoryWarning]; }

- (void)dealloc {

[super dealloc]; }

@end

Quasi tutti i metodi sono commentati e, a secondadelle necessità, potrete decommentarli, o ancherimuoverli (non rimuovete gli ultimi due) vedremonel prossimo articolo i loro possibili utilizzi. È all'interno di quest'ultimo file che scriveremo ilcodice che gestirà qualunque tipo di operazione;ogni volta che inseriremo la signature di un deter-minato metodo all'interno del file .h è logicamentenecessario creare la sua implementazione, quindi ilsuo codice effettivamente funzionante, all'internodel file .m. In questo caso è stata creata (automati-camente) una classe associata ad un viewcontroller,che utilizzeremo per gestire l'interazione con

l'utente, ma è possibile anche crearne un'altra deri-vante da UIView e associarla alla view contenutanel viewcontroller, e demandare a questa la gestio-ne dell'interazione con l'utente (ricordiamo chequalunque componente visuale può avere associa-ta una classe identificata da un file .h e .m);l'affermazione precedente è valida poiché entram-be le classi derivano da una comune, chiamata UIResponder, che consente di monitorare la pressioneda parte dell'utente fornendo ad entrambe alcunimetodi: touchesBegan, touchesCancelled, touchesEnded, touchesMoved. Il viewcontroller intercettaogni input (il tocco con una o più dita), poiché inquesto progetto la nostra UIView è utilizzata sem-plicemente come contenitore di altri componentivisuali (infatti non è stata creata alcuna classe chene rappresenti il suo Controller del pattern MVC).Se vogliamo quindi consentire l'interazione umanacon il ViewController, che contiene la nostra View,dovremo effettuare alcuni passi utilizzando IB. Inprimis creeremo un file .xib (in questo caso è statocreato automaticamente), poi due file .h e .m, cheestendono la classe che deve essere associata alcomponente grafico (anche in questo caso è statocreato automaticamente), nel nostro caso unUIViewController. Sarà quindi necessario intercet-tare la pressione dell'utente, oppure richiamareuno o più metodi se è stato premuto un pulsante oaltro componente interattivo (keyword IBActionoppure adoperando delegate). Se desideriamo acce-dere facilmente nei nostri metodi a uno, o più ele-menti (pulsanti, label, webview etc) presenti nelViewController dovremo, utilizzando la keywordIBOutlet, associare una variabile nel file .h a taleelemento grafico. Il passo successivo consisterà nelcollegare all'interno di Interface Builder questanuova classe con il componente visuale (facendouso dell'Identity Inspector), quindi collegare(opzionalmente) quali metodi vengono richiamatiquando l'utente interagisce con un determinatopulsante/componente interattivo. Sembra compli-cato ma dopo qualche incertezza diventerà unautomatismo che eseguirete con estrema velocità.L'operazione di associazione di un compoenentevisuale a una classe è molto semplice: basta selezio-nare il componete grafico prescelto nel DocumentWindow e successivamente selezionare la classe,precedentemente creata, all'interno del campoClass, contenuto nell'Identity Inspector. Nel prossi-mo articolo inseriremo un pulsante, una label euna webview, collegandoli tra loro utilizzandoIBAction e IBOutlet, mostreremo l'alternativa forni-ta dall'utilizzo di delegate, gestendo tutto all'internodel file .m del ViewController. Forniremo inoltreuna descrizione delle diverse tipologie di compo-nenti grafici presenti.

Andrea Leganza

MOBILE � Imparare a programmare l’Apple iPhone

ht tp ://www. ioprogrammo. i t

G 36 /Giugno 2009

L’AUTORE

Laureato in IngegneriaInformatica, da oltre un

decennio realizza soluzionimultimediali e non su

piattaforme e con linguaggidiversi. Certificato Adobe

ACE - Adobe Flex 3 and AIRCertificatd Expert, EUCIP

Core, appassionato difotografia, lingua

giapponese e istruttore dinuoto FIN, è attualmente

impegnato in numerosiprogetti multidediali con

alcune società nazionali edinternazionali; ècontattabile su

[email protected] odirettamente sul sito

www.leganza.it.

030-036:088-093-corsi-xsl 27-04-2009 18:05 Pagina 36

Page 11: 86746175 iPhone Programming

11 iPhone programming

iPhone programmingComponenti grafici e controllo UIWebView

APPLE IPHONEPROGRAMMINGCONTINUIAMO IL CORSO INERENTE LA PROGRAMMAZIONE DEL DISPOSITIVO SMARTPHONEAPPLE IPHONE, ILLUSTRANDO GLI STRUMENTI NECESSARI PER COMPLETARE L’APPLICAZIONEMINI BROWSER AVVIATA NEL NUMERO PRECEDENTE DELLA RIVISTA

Nell'articolo del numero precedenteabbiamo creato un progetto View-BasedApplication utilizzando XCode, fornendo,

inoltre, una breve descrizione di InterfaceBuilder; in questo nuovo appuntamento, posi-zioneremo i componenti grafici, un bottone, uncampo di testo e una UIWebView, ovvero uncomponente visuale che si comporta come unbrowser web, al pari del software Safari installatonell'iPhone. Seguiremo le procedure necessarieper rendere accessibili tali elementi all'internodella classe (file .h e .m) del ViewController,scrivendo infine il codice utile per farli interagiree rispondere agli eventi generati dal tocco dell'u-tente. L'applicativo consentirà, al termine di

questo articolo, di digitare un qualunque indiriz-zo web nel campo di testo, rendendolo cosìvisualizzabile nella UIWebView.

DISPONIAMO GLI ELEMENTI GRAFICIAvviamo Interface Builder facendo doppio clicsul file ioProgrammoArt2ViewController.xib, ci sipresenterà a video l’interfaccia di Fig.2Trasciniamo i tre componenti all'interno dellaUIView e ridimensioniamoli utilizzando i punti

presenti ai bordi: alcuni componenti non posso-no subire variazioni in alcune direzioni, comeavviene per il campo di testo che può variare dilunghezza e non di altezza.Per il bottone basterà fare doppio clic sull'areache gli compete per impostare la stringa che

CORSO MOBILE � Componenti grafici e controllo UIWebView

ht tp ://www. ioprogrammo. i t

G 30 /Luglio 2009

❑ CD ❑ WEBcorsoiphone2.zip

cdrom.ioprogrammo.it

Conoscenze richiesteOOP

SoftwareMac OS X 10.5 osuperiore

Impegno

¥Tempo di realizzazione

REQUISITI

Fig. 1: L'interfaccia che realizzeremo in questo articoloFig. 3: I punti utilizzabili per il ridimensionamento di un campo di testo

Fig. 2: Come si presente il file xib senza componentiaggiuntivi

030-036:088-093-corsi-xsl 28-05-2009 17:53 Pagina 30

Page 12: 86746175 iPhone Programming

iPhone programming 12

iPhone programming Componenti grafici e controllo UIWebView

ht tp ://www. ioprogrammo. i t Luglio 2009/ 31 G

Componenti grafici e controllo UIWebView � MOBILE

verrà visualizzata come label, oppure aprirel'Attribute Inspector (CTRL +1) e impostare ilcampo Title; per impostare il colore verde dellaview principale basta selezionarla e cliccare sultasto Background, che in questo caso ha assuntovalori RGB 201,255,156. La classe di appartenen-za di componenti visuali appena inseriti, lapotrete identificare analizzando la colonna Typedel Document Inspector di IB: UITextField,UIWebView e UIButton; queste tre informazionici permetteranno di creare delle variabili ad-hoc

nel prossimo paragrafo per utilizzarle nel codiceObjective-C.Da questa immagine si ponga anche attenzioneal tipo di struttura che abbiamo creato. La UIView principale, quella associata al nostroUIViewController, risulta la radice di un albero icui tre figli sono proprio i tre componenti cheabbiamo trascinato al suo interno. Parliamo ora di IBOutlet e IBAction, keyword checi agiscono come bridge tra Interface Builder eXCode.

COS’È IBOUTLETOra che abbiamo disegnato quella che sarà lanostra interfaccia grafica, dobbiamo realizzare ilcodice necessario per il funzionamento delnostro applicativo, quella responsabile delController, scrivendo istruzioni Objective-Call'interno della classe chiamata ioProgrammoArt2ViewController.m; prima di fare ciò è necessa-rio spiegare come creare un collegamento tra icomponenti visuali e oggetti in linguaggioObjectice-C, oggetti con i quali potremo interagi-re effettuando chiamate via codice: è in questafase che viene creato un collegamento esplicitotra View e Controller. Il modo più semplice con-siste nell'effettuare una procedura costituita dadue semplici passi: creare tante variabili quantisono i componenti grafici con i quali vogliamocomunicare, tre in questo caso, all'interno del fileresponsabile dell'interfaccia della classe, quello

con estensione .h, anteponendo alla classe diappartenenza usata nella dichiarazione dellavariabile, il termine IBOutlet, acronimo diInterface Builder Outlet; IBOutlet è un qualificato-re di tipo utilizzato solo per questo scopo, è quin-di una semplice macro vuota corrispondente allariga in linguaggio C #define IBOutlet; tale istruzio-ne, quando letta da Interface Builder, lo informache vogliamo realizzare una connessione travariabile Objective-C e oggetto visuale:

//file ioProgrammoArt2ViewController.h

#import <UIKit/UIKit.h>

@interface ioProgrammoArt2ViewController :

UIViewController

{

IBOutlet UIWebView *webView;

IBOutlet UITextField *addressField;

IBOutlet UIButton *goButton;

}

@end

Le tre variabili hanno come classe di apparte-nenza la stessa dei componenti visuali, chepotrete identificare sotto la colonna Type delDocument Inspector di IB, come è stato mostra-to nel paragrafo precedente. I tre IBOutlet sonocaratterizzati da una tipizzazione forte (stronglytyped) proprio a indicare che a queste variabilipossono essere associati solo elementi graficidelle classi da noi indicate; una versione menorestrittiva (weakly typed) verrà mostrata successi-vamente, quando forniremo una versione piùgenerica di IBAction (che nel prossimo articoloverrà spiegato approfonditamente). Le variabilicreate hanno come modificatore di accesso didefault @protected, inaccessibili all'esterno, mautilizzabile dalle classi che derivano dalla nostra(che in questo contesto è ininfluente, in un arti-colo successivo, descriveremo, tutti i modificato-ri disponibili). Questa prima parte della proce-dura ha un effetto immediato nella finestra delleproprietà del componente visuale chiamato FileOwner, al quale è associata la classe ioProgrammoArt2ViewControl ler; accedendo quindi all'Identity Inspector (CTRL+4) ritroveremo le trevariabili, all'interno della sezione chiamata ClassOutlets, esposte dalla nostra classe.La seconda, e ultima parte della procedura, con-siste nel premere il tasto destro del mouse dopoaver selezionato la riga corrispondente al FileOwner, a cui è associata la classe ioProgrammoArt2ViewController: tale operazione presenteràuna finestra semitrasparente nera nella qualetroveremo di nuovo le tre variabili appena

Fig. 4: La classe dei singoli componenti è visibilein corrispondenza della colonna type

NOTA

RIFERIMENTI WEBPer la creazionedell'account e perscaricare l'SDK, visitate ilseguente percorso web:http://developer.apple.com/iphone

NOTA

IL COMPONENTEUIWEBVIEWQuesto componente svolgela funzione di browser web,consentendo di visualizzarepagine internet nel nostroapplicativo. Consenteanche di consultare altritipi di documenti, tra cuiPDF, XML, immagini e altri:è possibile considerarlouna panacea in situazioniin cui si vuole svolgere ilpiù velocemente unprogetto, per limitazionidell'API o per espressavolontà

030-036:088-093-corsi-xsl 28-05-2009 17:53 Pagina 31

Page 13: 86746175 iPhone Programming

13 iPhone programming

iPhone programmingComponenti grafici e controllo UIWebViewMOBILE � Componenti grafici e controllo UIWebView

ht tp ://www. ioprogrammo. i t

G 32 /Luglio 2009

aggiunte alla classe. Basterà trascinare il cer-chietto, posizionato a destra, sul componentevisuale che vogliamo associare.Tale operazione deve essere ripetuta per le trevariabili, associando a ognuna il rispettivo com-

ponente visuale. Abbiamo così completato laprocedura necessaria per accedere, nel fileioProgrammoArt2ViewController.m, a tali oggetti

visuali e poterne così modificarne stato e aspet-to; è l'iter più semplice da seguire quando si ini-zia a lavorare con i file .xib. È inoltre possibileinvertire tale procedura lasciando che InterfaceBuilder crei una classe partendo dall'interfacciache avete creato, operazione che risulta di gran-de utilità quando si hanno molti oggetti visuali enon si desidera inserirli manualmente, oppure siè deciso di realizzare prima il file xib della classeassociata: questa ulteriore procedura verrà spie-gata, ma in un prossimo articolo.

LA COMUNICAZIONE TRA GLI OGGETTIIn ambito object oriented la comunicazione tradue classi avviene principalmente attraversol'invocazione di metodi: in Objective-C tale ope-razione prende il nome di invio di messaggi. Il design pattern, utilizzato per gestire gli eventigenerati da un componente grafico, prende ilnome di target-action; quando un utente interagi-sce con un componente lo induce a inviare unmessaggio, chiamato action, a un altro oggetto,chiamato target, responsabile della sua gestione.È possibile procedere in modi diversi, a secondadella classe da cui derivano tali componenti:bottoni (UIButton) e campi di input di testo(UITextField) ad esempio, possono richiamare,dei metodi presenti in una istanza di una classeutilizzando il qualificatore di tipo IBAction, ese-guendo una procedura molto simile a quella uti-lizzata nel paragrafo precedente (IBOutlet). Per visualizzare l'elenco degli eventi che vengo-no lanciati da un componente visuale è suffi-ciente premere il pulsante destro sul relativooggetto presente nel Document Inspector diInterface Builder; con altri componenti si adope-ra la tecnica del delegate; una terza soluzione,disponibile per i componenti che discendono daUIControl, come UIButton, UITextField UIDatePicker e altri, consiste nell'utilizzare i selector,adoperando istruzioni Objective-C; per ora trat-teremo solamente di IBAction.

L’INTERFACE BUILDERACTION - IBACTIONCon IBAction, acronimo di Interface BuilderAction, identifichiamo quei metodi cheInterface Builder mostrerà all'interno della pro-pria interfaccia grafica quando è selezionata laclasse in cui le abbiamo create, all'interno dellasezione Class Actions del pannello IdentityInspector di IB (CTRL+4). Tali metodi rivestiran-no il ruolo di gestori degli eventi lanciati dai

Fig. 6: La finestra semitrasparente

Fig. 5: Dopo aver inserito le tre variabili all'interno delfile .h, queste saranno visualizzate nell'Inspector dellaclasse in Interface Builder

NOTA

IBOUTLETIBOutlet viene utilizzato pernotificare Interface Builder

che tale variabile dovràessere utilizzata all'interno

dell'interfaccia grafica,svolge quindi la funzione diconnessione tra Controller

e View, instaurando uncollegamento bidirezionale.

030-036:088-093-corsi-xsl 28-05-2009 17:54 Pagina 32

Page 14: 86746175 iPhone Programming

iPhone programming 14

iPhone programming Componenti grafici e controllo UIWebView

ht tp ://www. ioprogrammo. i t Luglio 2009/ 33 G

Componenti grafici e controllo UIWebView � MOBILE

componenti visuali. IBAction viene utilizzato alposto del tipo di ritorno della funzione, è infat-ti una macro, sinonimo di void (#define IBActionvoid), e per tale motivo tali metodi non restitui-scono alcun valore. Non è necessario creareuna corrispondenza uno a uno tra un metodoIBAction e un evento richiamato da un singolooggetto visuale, è possibile associare tale codicea un numero qualsiasi di eventi lanciati daaltrettanti componenti visivi. Nel nostro pro-getto abbiamo necessità di gestire la pressionedel pulsante, che provvederà in risposta a taleevento, a prelevare il valore del campo di inputdi testo e lo utilizzerà per aprire la pagina rela-tiva all'interno del webView, per tale motivo lachiamiamo gotoAddress:

//file ioProgrammoArt2ViewController.h

#import <UIKit/UIKit.h>

@interface ioProgrammoArt2ViewController :

UIViewController

{

IBOutlet UIWebView *webView;

IBOutlet UITextField *addressField;

IBOutlet UIButton *goButton;

}

-(IBAction) gotoAddress;

@end

Abbiamo così esposto, semplicemente inseren-dola nel file di interfaccia e identificandola conIBAction, tale funzione in Interface Builder. La funzione ci sarà presentata nell'area chiamataClass Actions di Interface Builder, nella sezioneomonima dell'Identity Inspector:ll passo successivo consiste nell’associazionedella funzione alla pressione del pulsante cheabbiamo creato, operazione identica a quella uti-lizzata con IBOutlet; basterà quindi premere iltasto destro sulla riga corrispondente a FileOwner per notare la disponibilità all’interno dellacategoria Received Actions

A questo punto la domanda potrebbe sorgerespontanea: perché viene inserito in questacategoria, e non sotto Class Actions, così comeavviene nell'Identity Inspector? Questo perchè lapressione del tasto effettua una chiamata alnostro metodo e, dal punto di vista della classe,tale chiamata significa ricevere un'azione (unmessaggio) da parte di un oggetto esterno, nelnostro caso un componente visivo; non fateviconfondere, quindi, sono sinonimi. Trasciniamo il cerchietto sul pulsante, ci verràpresentato un elenco di eventi gestiti dal botto-ne, selezioniamo Touch Up Inside, (evento gene-rato quando l'utente solleva il dito dallo scher-mo dopo aver premuto il nostro pulsante). A questo punto non ci resta che scrivere il codi-ce del metodo gotoAddress, ma prima sarà neces-sario spiegare come si invoca un qualunquemetodo appartenente a un oggetto Objective-C.

LA GESTIONE DEI METODIObjective-C si differenzia principalmente da altrilinguagg come Java, C#, Actionscript 3 poiché uti-lizza un approccio differente per il passaggio diparametri. Per implementare un metodo è necessario inserirea priori la sua signature, costituita da tipologia, tipodel valore restituito, nome, due punti, elenco e tipodei parametri, nel file di interfaccia, come abbiamofatto per -(IBAction) gotoAddress, terminata dalpunto e virgola, poi scrivere il metodo completo dicodice nel file di implementazione, quello conestensione .m. Un metodo che non accetta parametri verrà scrittoall'interno del file di interfaccia (.h) nel seguentemodo:

//file .h

- (void) nomeMetodo;

e verrà invocato tramite la sintassi:Fig. 7: La presenza dell'IBAction è ora mostrata anchein questa finestra

Fig. 8: L'effetto del trascinamento del cerchietto relativo all'IBAction sul pulsante

NOTA

VIEW-BASEDAPPLICATIONÈ il progetto più “comodo”per iniziare a programmareper iPhone/iPod Touch,infatti, fornisce una finestracontenuta all'interno di uncontroller, del quale vienefornita già una classe diimplementazione checonsente al suo interno digestire la parte diController, del patternModel View Controller,responsabile, quindi, dellagestione degli eventi edello stato del nostroapplicativo.

030-036:088-093-corsi-xsl 28-05-2009 17:54 Pagina 33

Page 15: 86746175 iPhone Programming

15 iPhone programming

iPhone programmingComponenti grafici e controllo UIWebView

//file .m

[oggetto nomeMetodo];

È quindi sempre necessario racchiudere traparentesi quadre sia l'oggetto che il nome delmetodo invocato. Nel caso restituisca un intero:

//file .h

- (int) nomeMetodo;

sarà possibile utilizzare tale risultato associan-dolo a una variabile nel seguente modo:

//file .m

int variabile = [oggetto nomeMetodo];

Il simbolo –, che precede ogni metodo, indicache questo è associato all'istanza, sostituendolocon un + lo si associa alla classe, verrà quindiinvocato nel seguente modo:

//file .m

int variabile = [nomeClasse nomeMetodo];

In questi articoli, a meno di precisa motivazione,avremo sempre la necessità di creare metodi perconsentire la chiamata a una istanza di oggetto,utilizzeremo perciò il -. Esiste un modo per associare un modificatore diaccesso (public, protected, private), a un metodo?Non esplicitamente, per rendere un metodo pri-vato si possono usare le categorie, ma non èargomento di questo articolo: l'importante, perora, è essere consapevoli che qualunque metodoè visibile all'esterno, da parte delle altre classi, ameno di utilizzare degli accorgimenti particolari,e che se non scriverete nell'implementazionedella classe tutti i metodi dichiarati nell'interfac-cia, verrete avvisati con il seguente warning:

warning: incomplete implementation of

class 'ioProgrammoArt2ViewController'

warning: method definition for '-

nomeMetodo:' not found

Nel caso un metodo accetti un parametro, si uti-lizza la seguente sintassi:

//file .h

-(void) nomeMetodo: (tipoSingoloParametro) par1;

Qui si nota la netta differenza con i linguaggiObject Oriented di “nuova” generazione; analiz-zandolo notiamo che i parametri seguono il sim-bolo dei due punti, poi si evince che non è pre-sente un nome per il primo parametro, questo hasolo associati il tipo di appartenenza e il nome

(alias) che assumerà all'interno del corpo delmetodo. Il calcolo del fattoriale di un numeroavrà questa signature:

//file .h

-(double) FattorialeDi: (int) par1;

Che sarà utilizzato in questo modo:

//file .m

int i = 5;

double risultato = [oggetto FattorialeDi:i];

Con più parametri, due in questo caso, si utilizza:

//file .h

-(void) nomeMetodo: (tipoPrimoParametro) par1

nomeSecondoParametro: (tipoSecondoParametro) par 2;

Se volessimo realizzare un metodo che effettua lasomma tra due interi, si potrebbe utilizzare quin-di la seguente sintassi:

//file .h

-(void) Somma: (int) par1 con: (int) par 2;

Da invocare nel seguente modo:

//file .m

int i = 100, j = 50;

int result = [oggetto Somma:i con:j];

È possibile effettuare chiamate in cascata comenel seguente esempio:

int result = [oggetto Somma:[oggetto Somma:i con;

j] con: J];

Il cui risultato è la somma (i+j)+j; il numero dellechiamate è teoricamente illimitato, ma taleapproccio ha il difetto di incrementare i rischi di

errori per la necessità di porre attenzione alla posi-zione delle parentesi quadre, sia aperte che chiuse:XCode ci viene in aiuto, infatti, posizionando ilcursore su una parentesi, verrà automaticamenteevidenziata quella di chiusura corrispondente (odi apertura nel caso selezionassimo l'altra).

MOBILE � Componenti grafici e controllo UIWebView

ht tp ://www. ioprogrammo. i t

G 34 /Luglio 2009

Fig. 9 Il sistema d’evidenziazione della parentesi quadra

NOTA

IBACTIONIBAction viene utilizzato per

identificare un metodo diuna classe, in modo da

notificare Interface Builderche tale blocco di codicepotrà essere invocato daun componente grafico;

allo scaternarsi di uno o piùeventi consente, inoltre, di

esporre tale metodoall'interno di quelli forniti

da un determinato oggettovisuale di IB. Ciò avviene

quando si preme il destrosulla relativa riga neldocument inspector.

030-036:088-093-corsi-xsl 28-05-2009 17:54 Pagina 34

Page 16: 86746175 iPhone Programming

iPhone programming 16

iPhone programming Componenti grafici e controllo UIWebView

ht tp ://www. ioprogrammo. i t

IL CODICE DI GOTOADDRESSOra che abbiamo spiegato come invocare unmetodo generico, dovremo semplicemente pre-levare il testo del campo addressField e passarlocome indirizzo a webView. Creiamo il metodoall'interno del file ioProgrammoArt2ViewControl -ler.m, nel codice identificato dal blocco @imple-mentation … @end:

//file ioProgrammoArt2ViewController.m

#import "ioProgrammoArt2ViewController.h"

@implementation ioProgrammoArt2ViewController

-(IBAction) gotoAddress{

//Corpo del metodo

}

@end

Per prelevare il testo dal campo di input e aprirela pagina web (se esistente), basterà utilizzare ilseguente codice:

-(IBAction) gotoAddress{

//Preleviamo l'indirizzo

NSString *address = [addressField text];

//Creiamo un indirizzo utilizzando tale stringa

NSURL *url = [NSURL URLWithString:address];

//Creiamo una richiesta con tale indirizzo

NSURLRequest *request = [NSURLRequest

requestWithURL:url];

//Carichiamo la pagina utilizzando tale richiesta

[webView loadRequest:request];

}

Quando “cliccheremo” sul campo di testo si pre-senterà la tipica tastiera e, successivamente, allapressione del bottone verrà caricata la paginarichiesta. È possibile raggruppare le chiamateeffettuate in una sola riga:

[webView loadRequest:[NSURLRequest

requestWithURL:[NSURL URLWithString:[addressField

text]]]];

Come detto precedentemente, questa versione delcodice risulta essere poco leggibile, a meno di nonaver maturato una certa esperienza. Abbiamocompletato la prima fase di questo semplice pro-getto. Ora, iniziamo a spiegare in dettaglio alcunecaratteristiche dell'Objective-C e dell'interfaccia-mento con Builder, perché ci consentiranno diaggiungere ulteriori funzionalità nel prosieguo.

IBACTION E IDIl precedente utilizzo di IBAction ha la limitazio-ne di non fornire alcuna informazione sull'og-

getto che lo ha invocato, nel nostro caso il botto-ne chiamato goButton, per tale motivo è disponi-bile una versione più completa, che nel nostroesempio sarà la seguente:

/file ioProgrammoArt2ViewController.h

- (IBAction)gotoAddress:(id)sender;

Abbiamo semplicemente aggiunto un parame-tro, di tipo id di nome sender che potremo utiliz-zare all'interno del blocco di codice del metodoper avere informazioni sull'oggetto che ha invo-cato tale comando. Questa funzionalità permettedi condividere un IBAction tra un numero illimi-tato di oggetti, non solo di numero, ma anche ditipo, spetterà a noi analizzare la variabile senderper capirne la tipologia. Un tipico esempio di uti-lizzo è quello in cui abbiamo numerosi bottoni edesideriamo avere una sola zona di codiceresponsabile della loro gestione, utilizzandosender potremo identificare chi è il chiamante e,se necessario, modificarlo opportunamente.Esiste un'ulteriore versione, la più dettagliatadisponibile, nella quale abbiamo modo anche diidentificare l'evento che l'ha invocata:

/file ioProgrammoArt2ViewController.h

- (IBAction)gotoAddress:(id)sender

Event:(UIEvent*)event;;

Tale variabile risulta utile se decidiamo di associ-re il metodo contemporaneamente a più eventi,utilizzando, ad esempio, Touch Up Inside e TouchDown, eventi “scatenati”, in occasioni diverse, dalcontrollo afferente UITextInput. Il “difetto” di tali metodi è dovuto al tipo del para-metro utilizzato, id, il che rappresenta il tipo piùgenerale possibile, che, se da un punto di vistaconsente grande libertà su quale componenteutilizzare per invocare tale IBAction, dall’altrointroduce alcune problematiche, principalmen-te riguardanti l'accesso ai campi e a i metodi del-l'oggetto chiamante. Nel prossimo articolo a taleconcetto dedicheremo particolare attenzione;l’utilizzo consapevole di id consente di accederealla estrema versatilità fornita dal linguaggioObjective-C.

ALLA PROSSIMA...Creata una versione funzionante dell'appli-cativo, nel prossimo articolo del corso, lomodificheremo per fornire ulteriori funzionalità.Faremo così conoscenza con altri argomenticorrelati. Buona programmazione.

Andrea Leganza

Luglio 2009/ 35 G

Componenti grafici e controllo UIWebView � MOBILE

L’AUTORE

Laureato in IngegneriaInformatica, da oltre undecennio realizza soluzionimultimediali e non supiattaforme e con linguaggidiversi. Certificato AdobeACE - Adobe Flex 3 and AIRCertificatd Expert, EUCIPCore, appassionato difotografia, linguagiapponese e istruttore dinuoto FIN, è attualmenteimpegnato in numerosiprogetti multidediali conalcune società nazionali edinternazionali; ècontattabile [email protected] odirettamente sul sitowww.leganza.it

030-036:088-093-corsi-xsl 28-05-2009 17:54 Pagina 35

Page 17: 86746175 iPhone Programming

17 iPhone programming

iPhone programmingComponenti grafici e controllo UIWebView

CREARE SOFTWAREPER L’APPLE IPHONEIN QUESTA TERZA LEZIONE MODIFICHIAMO IL MINIBROWSER INTRODOTTO NEL PRECEDENTEARTICOLO DELLA SERIE, INTRODUCENDO ALTRI CONCETTI FONDAMENTALI COME LAGESTIONE DEGLI ASPETTI GRAFICI E L’UTILIZZO DEL COMPONENTE UIWEBVIEW

Nel precedente articolo abbiamo comple-tato un'interfaccia minimale e abbiamorealizzato la logica necessaria per il suo

corretto funzionamento, ora ci dedicheremoall'introduzione di ulteriori caratteristiche dellinguaggio Objective-C; aggiungeremo un'ani -ma zione per monitorare il caricamento dellapagina web richiesta.

IL TIPO IDNella seconda versione del metodo gotoAddress,introdotto nell'articolo precedente, che quiriproponiamo per semplicità,

//file ioProgrammoArt2ViewController.h

(IBAction)gotoAddress:(id)sender;

abbiamo utilizzato un parametro di tipo id, checi ha consentito di rendere il più generale possi-bile tale chiamata, in grado di essere invocata daqualunque componente visuale (ma non solo, ecapirete il perché a breve): è necessario discuter-ne approfonditamente, poiché incontrerete id sianell'API che nella documentazione; id, attenzio-ne è privo dell'asterisco, simbolo utilizzato quan-do ci si riferisce alle istanze delle classi, esisteperché Objective-C è un linguaggio a tipizzazionedinamica, ciò significa che solo a runtime, in ese-cuzione, il vostro software avrà realmente mododi capire se una variabile appartiene ad un deter-minato tipo di dato, avrà quindi una strutturaderivante alla sua catena di ereditarietà, parten-do dalla root (in genere è NSObject), e scendendoprogressivamente, risultando perciò corredatoda precisi metodi e variabili; questo comporta-mento è completamente diverso da quello chepresente in JAVA e C#, linguaggi a tipizzazione sta-tica, dove tale controllo avviene in fase di compi-lazione, e durante il quale si riceve un errore nelcaso si assegni a un'istanza di una classe un tipodiverso da quello della sua catena di ereditarietà

(attribuendo ad esempio, il contenuto di unavariabile String a una istanza di una classe creataestendendo un componente Swing) oppure sirichiama un metodo inesistente (con parametridi tipo e/o numero diversi); in Objective-C sevengono rilevati dei conflitti o delle omissioni siriceve solamente un avviso, un warning in fase dicompilazione, bisogna prendere consapevolezzache in tale contesto tutto è incerto riguardo unavariabile tranne quando si è in esecuzione; sologli errori sintattici, come parentesi e punti e vir-gola mancanti, interrompono il processo di com-pilazione. Associando, ad esempio, una variabiledi tipo UITextField a una di tipo UIWebView, ope-razione concettualmente errata, ma perfetta-mente lecita in questo linguaggio:

UIWebView *oggettoBrowser;

UITextField *oggettoCampoDiTesto= oggettoBrowser;

riceveremo il seguente avviso:

warning: 'initialization from distinct Objective-C type'

Per i metodi il discorso è molto simile: il compi-latore, se richiameremo un metodo inesistente, osconosciuto al momento di compilazione comenel seguente caso:

//Definizione della variabile

UILabel *oggettoLabel;

//inizializzazione della variabile

...

//Chiamata del metodo

[oggettoLabel metodoInesistente];

segnalerà la mancanza di informazioni a riguar-do con il seguente avviso

warning: 'UITextField' may not respond to 'metodoInesistente'

CORSO MOBILE � Componenti grafici e controllo UIWebView

ht tp ://www. ioprogrammo. i t

G 48 /Agosto 2009

❑ CD ❑ WEBiphonecorso4.zip

cdrom.ioprogrammo.it

Conoscenze richiesteOOP

SoftwareMac OS X 10.5 osuperiore

Impegno

¥Tempo di realizzazione

REQUISITI

048-053:088-093-corsi-xsl 30-06-2009 16:13 Pagina 48

Page 18: 86746175 iPhone Programming

iPhone programming 18

iPhone programming Componenti grafici e controllo UIWebView

ht tp ://www. ioprogrammo. i t Agosto 2009/ 49 G

Componenti grafici e controllo UIWebView � MOBILE

Notate il may utilizzato per denotare tale incer-tezza al momento della compilazione. Anchesbagliare la chiamata, invertendo “semplicemen-te” il tipo dei parametri, vi verrà segnalato comeun warning, e non un errore, il compilatore pre-suppone che sappiate cosa stiate facendo e siaspetta, a runtime, di trovare questo metodo contale diversa signature. Il vostro software verràcomunque compilato e avviato senza alcunalimitazione, il problema si presenterà a runtime eil risultato sarà uno, o più crash, con il conse-guente avvio del debugger (spesso molto vagosulla causa scatenante del crash). Attenzionequindi ai warning del compilatore, perché sono ilprimo segnale di un errore semantico e quindi diun possibile crash! Da quanto detto, si haun'estrema libertà (anche di sbagliare) nell'edi-tor, ma si è di conseguenza poco “assistiti” dalcompilatore per prevenire molti tra gli errori piùcomuni, che si erano risolti con i linguaggi dinuova generazione, per merito dei loro controllipreventivi.Per merito di questa dinamicità intrinseca al lin-guaggio, è possibile utilizzare un “placemark”, unindicatore, per segnalare che un determinatooggetto al momento della compilazione nonappartiene a una classe predefinita, perché non èconosciuta a priori o non la si vuole specificare:in entrambi i casi si utilizza id come tipo dellavariabile. Nella documentazione relativa aIBAction viene utilizzato id per la signature, perindicarne la completa indipendenza dalla classechiamante; id ha inoltre avuto il pregio di sem-plificare il processo di creazione dell'API, evitan-do di realizzare un metodo per ogni oggetto chia-mante, poiché potrebbe appartenere a una qual-siasi delle decine di classi disponibili, UIButton eUITextField e via discorrendo; in casa Apple ci siè mantenuti il più generale possibile, lasciandoquindi al programmatore piena libertà in chemodo gestire tale chiamante. Id ha comunquedelle limitazioni, a livello di usabilità: se si defini-sce una variabile di questo tipo non è possibileavere un elenco preciso, utilizzando il sistema diCode Completition, delle funzioni e delle varia-bili appartenenti a tale oggetto, poiché XCodenon ha modo di sapere su che classe specifica sista lavorando, riceveremo perciò un elenco esau-stivo di tutti i metodi disponibili per tutte le clas-si presenti nell'API: migliaia di metodi, variabili ecostanti, una lista di scarsa utilità! Id può generare confusione nel codice sel'utilizzo dell'oggetto associato non viene oppor-tunamente descritto, oltre a incrementare ilnumero di possibili bug, poiché si potrebberochiamare metodi, e provare ad accedere a varia-bili, non presenti a runtime. Come è possibilelimitare l'elenco dei metodi, e dei campi, sugge-

riti da XCode quando siamo certi del tipo divariabile che id assumerà a runtime? Nel nostrocaso è del tipo UIButton, un bottone, passatocome parametro sender nel metodo IBAction. Lasoluzione è molto semplice, basta creare unavariabile del tipo desiderato e associarle il para-metro sender; se pensiamo di utilizzarla un certonumero di volte, potremo utilizzare nel blocco dicodice il seguente codice:

-(IBAction)gotoAddress:(id)sender {

UIButton *mioBottone = sender;

[mioBottone metodoUno];

[mioBottone metodoDue];

...

}

Oppure utilizzare il casting:

-(IBAction)gotoAddress:(id)sender {

[(UIButton *)sender metodoUno];

[(UIButton *)sender metodoDue];

...

}

Quest'ultima tecnica dovrà essere utilizzata perogni chiamata effettuata e/o per accedere ad uncampo della classe, allo scopo di “forzare”l'editor a mostrare il corretto, e limitato, elencodi metodi e variabili; questa tecnica risulta peròalquanto scomoda, degradando la leggibilità delcodice, oltre ad incrementare il numero di carat-teri da digitare. Se volessimo evitare di utilizzareid in questo contesto, potremmo semplicementesostituirlo con il tipo corrispondente al compo-nente grafico chiamante, UIButton nel nostrocaso:

-(IBAction)gotoAddress:(UIButton *)sender {

[sender metodoUno];

[sender metodoDue];

...

}

In questo modo l'operazione di casting verràeffettuata automaticamente. Applicando unaqualunque di queste soluzioni, riceveremo sug-gerimenti coerenti con la classe che abbiamospecificato. L'unica limitazione dell'ultima solu-zione è che impedisce la condivisione di taleIBAction con quei componenti che non appar-tengono alla classe UIButton, rendendo quindiimpossibile qualunque associazione diversa daquella tramite Interface Builder. Id può essereutilizzato per qualunque variabile presente nellanostra classe, anche per gli IBOutlet; nell'articolo

RIFERIMENTI WEBPer la creazionedell'account e perscaricare l'SDK, visitate ilseguente percorso web:http://developer.apple.com/iphone

NOTA

IDid svolge la funzione diidentificatore generico diclasse; una variabile di taletipo può appartenere aqualunque classe e la suareale identità potrà essereevidenziata solo a tempo diesecuzione; ha il difetto dinon fornire informazioni seuno o più metodi, o campi,a cui si accede esistonoeffettivamente a runtime,ciò incrementa il rischio dibug e crash del sistema, senon viene utilizzato conaccortezza.

048-053:088-093-corsi-xsl 30-06-2009 16:13 Pagina 49

Page 19: 86746175 iPhone Programming

19 iPhone programming

iPhone programmingComponenti grafici e controllo UIWebViewMOBILE � Componenti grafici e controllo UIWebView

ht tp ://www. ioprogrammo. i tG 50 /Agosto 2009

precedente avevamo infatti detto che i treIBOutlet utilizzati erano a tipizzazione forte,(strongly typed), cioè limitavano i tipi di compo-nenti visuali che potevamo associare, evitandopossibili errori di collegamento in InterfaceBuilder. Con l'introduzione di id, possiamo orarendere più dinamiche tali associazioni, basteràmodificare il codice nel seguente modo:

IBOutlet id webView;

IBOutlet id addressField;

IBOutlet id goButton;

Questa versione, con id, chiamata anche a tipiz-zazione debole (weakly typed) è quindi la piùgenerica tra quelle disponibili e consente diassociare qualunque elemento visuale alle trevariabili: potremo quindi associare un campo ditesto (visuale) al nostro webWiew (variabile)senza che il compilatore o Interface Buildersegnali alcun problema; anche in questa situa-zione l'utilizzo indiscriminato di id, più che altroprivo di sufficiente conoscenza, rischia di incre-mentare il numero di possibili chiamate a meto-di o accesso a campi inesistenti, con la genera-zione di numerosi crash. Per soddisfare la curio-sità di alcuni sulla reale identità di id bisognaconsultare la documentazione e si verrà a cono-scenza che è un semplice puntatore a una strut-tura che contiene la “classe”, che verrà utilizzataa runtime:

typedef struct objc_object {

Class isa;

} *id

Tale Class è un puntatore a una struttura:

typedef struct objc_class *Class;

definita nel seguente modo:

struct objc_class {

struct objc_class *isa;

struct objc_class *super_class;

const char *name;

long version;

long info;

long instance_size;

struct objc_ivar_list *ivars;

struct objc_method_list **methodLists;

struct objc_cache *cache;

struct objc_protocol_list *protocols;

};

nella quale troviamo tutte le informazioni cherappresenteranno lo stato dell'istanza che idconterrà, tra cui metodi, variabili, protocolli, e

altri dettagli; passando attraverso id siamo quin-di giunti alla struttura che viene utilizzata perrealizzare in Objective-C, utilizzando il linguag-gio C, ogni classe che utilizziamo.Può venire il dubbio, in alcuni contesti, seNSObject, poiché è la root delle classi che abbia-mo fino ad ora utilizzato, è intercambiabile conid; la risposta a tale domanda è: dipende; id puòospitare qualunque istanza appartenente a unaclasse anche non discendente da NSObject(come NSProxy e Protocol); una qualunque chia-mata a un metodo, o accesso a un campo, di idnon viene verificata, e segnalata in caso di inesi-stenza o incongruenza, in fase di compilazione,mentre se attribuiamo a una variabile il tipoNSObject, riceveremo un warning se invochiamouno, o più, metodi non esposti da tale classe diroot; come è stato detto, il sistema di completa-mento del testo per id risulta praticamente inuti-lizzabile, presentando tutti i metodi disponibilinell'API, nel caso di NSObject, invece, ricevere-mo quei metodi presenti in tale classe, senzaperò ottenere quelli forniti dalla catena di eredi-tarietà: in entrambi i casi, per ottenere l'elencopiù coerente di metodi è necessario effettuare ilcasting alla classe più specifica desiderata, comeè stato mostrato negli esempi di gotoAddress.

DELEGATECon delegation, o message forwarding (conosciutoanche con i nomi di consultation e aggregation), siintende quella procedura che ha come risultatol'indirizzamento degli eventi che una classe generaverso un'altra, presente al proprio interno comeuna variabile, e che viene configurata per gestirliopportunamente; tale nome è usato per descrivereun design pattern, descritto in ingegneria delsoftware, che ha proprio questo comportamento;con delegate si intende una proprietà della classe,identificata con una variabile di tipo id, che vieneutilizzata per realizzare la delegation. Non tutte leclassi forniscono tale variabile, nel nostro progettoè disponibile per l'UITextField e UIWebView, ovvia-mente è la documentazione la fonte migliore perdeterminare se la supportano. Quando utilizzare ildelegate? Come abbiamo detto precedentemente,in questo contesto le classi svolgono la funzione diController dei componenti visuali, a volte però nonè necessario, o non si desidera, creare una classeche realizzi il Controller per ognuno di questi ele-menti, ma si vuole, comunque, gestire una certafamiglia di eventi inviati da tali istanze; un tipicoesempio è quello della UITableView, una tabellavisuale che mostra una serie di dati in sequenza; ingenere si fa delegare tutta la sua gestione, insiemealla visualizzazione delle proprie informazioni e

NOTA

IDENTIFICARE LA REALE CLASSE

DI IDLa necessità di utilizzo di id

è relativamente ridotta,conviene adoperarlo in

situazioni diindeterminatezza a

runtime; per identificare lareale classe di una sua

istanza, basta invocare sudi essa (più correttamente

“inviare un messaggio”)uno dei seguenti metodi:

- (BOOL)isKindOfClass:

(Class)aClass

e

–(BOOL)isMemberOf

Class:(Class)aClass;

questi due consentono diottenere un riscontro

immediato, fornendoci leinformazioni necessarie per

effettuare un casting alloscopo di scegliere

operazioni coerenti con lareale identità dell'istanza.

048-053:088-093-corsi-xsl 2-07-2009 16:49 Pagina 50

Page 20: 86746175 iPhone Programming

iPhone programming 20

iPhone programming Componenti grafici e controllo UIWebView

ht tp ://www. ioprogrammo. i t Agosto 2009/ 51 G

Componenti grafici e controllo UIWebView � MOBILE

all'interazione dell'utente, al UIViewController chela contiene (utilizzando il protocollo UITableViewDataSource di cui parleremo nel prossimoparagrafo). È possibile impostare il delegate indue modi: il primo consiste nell'utilizzareInterface Builder, premere il destro del mousesulla riga corrispondente al componente visualedel Document Inspector, noterete una riga contale nome, e potrete associare tale proprietà a unoggetto semplicemente trascinando il relativocerchietto, come è stato fatto per gli IBOutletL'altro consiste nell'utilizzare un comando in lin-guaggio Objective-C:

miaIstanza.delegate = oggettoDelegato;

Il risultato è identico, l'unica differenza è che nelsecondo caso si ha modo di controllare quandoinizializzare tale associazione, mentre nel primoavviene in un non precisato momento durante latrasformazione del file xib, che, come detto pre-cedentemente, è un file XML, nel codice Objective-C necessario per realizzare l’interfaccia gra-fica da inserire nell'eseguibile. Abbiamo cosìimpostato che l'oggetto miaIstanza delega la suagestione a oggettoDelegato. Impostare il delegatedi un'istanza di una classe non è però sufficienteaffinché tale delegato possa intercettare ed ela-borare gli eventi: è necessario che tale classeimplementi i metodi che verranno richiesti,diventi cioè conforme ad un protocollo. Il nomedi tale protocollo, corredato del relativo elenco dimetodi, lo si ritrova nella documentazione asso-ciata al parametro delegate della classe sullaquale volete applicare la delegation. Per sempli-ficare questa procedura è disponibile, nel casodella suddetta UITableView, una componentevisuale chiamata UITableViewController checonsiste proprio in un UIViewController e unaUITableView al suo interno, già configurato inquesto modo.

I PROTOCOLLIIl concetto di protocollo suonerà familiare amolti utilizzatori di linguaggi object-oriented dinuova generazione, perché in Java, come in C#,vengono chiamati Interfacce, mentre ClassiAstratte in C++. Diventare conforme (a volte siutilizza il termine adattarsi) a un protocollo, nellapratica significa garantire che una determinataclasse utilizza, ha quindi implementato, tutti imetodi dichiarati nel protocollo rispettando inomi, il tipo e il numero dei parametri utilizzati;il comportamento interno dei metodi non inte-ressa al protocollo, è demandato al programma-tore. Un esempio è quello dell'UITable ViewData

Source, citato nel paragrafo precedente quandoparlavamo della UITableView associata a unUIViewController, questo espone i seguenti meto-di (è solo un estratto):

@required

- (NSInteger)tableView:(UITableView *)table

numberOfRowsInSection:(NSInteger)section;

- (UITableViewCell *)tableView:(UITableView *)

tableView cellForRowAtIndexPath:(NSIndexPath *)

indexPath;

@optional

- (NSInteger)numberOfSectionsInTableView:

(UITableView *)tableView;

- (NSString *)tableView:(UITableView *)tableView

titleForHeaderInSection:(NSInteger)section;

- (NSString *)tableView:(UITableView *)tableView

titleForFooterInSection:(NSInteger)section;

...

come si può notare immediatamente, da unaprima analisi di tale codice, non viene inseritoalcun blocco di comandi Objective-C all'internodei metodi, ma troviamo semplicemente il nome ei parametri. Ogni protocollo è da considerare alpari di un “patto” con il quale si garantisce che sirispetta un certo standard, almeno riguardo i nomied i parametri. I protocolli vengono suddivisi indue categorie: formali ed informali: con i primi siindicato quelli che, utilizzando solo la keyword@required, obbligano l'implementa zione di tutti ipropri metodi, mentre con i secondi si forniscelibera scelta su quale sottoinsieme di metodi realiz-zare, indicazione che avviene utilizzando lakeyword @optional; dichiarando la conformità diuna nostra classe a uno o più protocolli formalirichiederà un controllo da parte del compilatore infase di compilazione, al contrario di quelli informa-li, che vengono completamente ignorati. Le inter-facce utilizzate in Java e/o C#, sono quindi compa-rabili ai protocolli formali. Ma perché in Objective-C è possibile avere tale libertà di scelta? Merito delfatto che, come è stato detto precedentemente, èun linguaggio a tipizzazione dinamica: anche ilcontrollo se una classe rispetta un determinatoprotocollo, avviene a runtime, utilizzando un pro-cedimento chiamato reflection, tramite il quale laclasse viene interrogata (o più correttamente siinterroga) se ha certe caratteristiche. Da quandodetto potremmo implementare tutti i metodinecessari per il protocollo UITableViewData Sourcesenza dichiararlo esplicitamente, tanto, se vengonorilevati in fase di esecuzione verranno comunqueinvocati. Analizzando i metodi presenti nella classedi root, NSObject, troviamo il seguente metodo:

- (BOOL)conformsToProtocol:(Protocol *)aProtocol

NOTA

DELEGATECon delegate si identificaquella istanza di una classeresponsabile della gestionedegli eventi lanciati da unaltro oggetto; risulta utilequando si realizzanointerfacce con numerosicomponenti visuali e sipreferisce centralizzare ilcodice all'interno di unnumero più ristretto diclassi.

NOTA

DEASOCCIAREIL DELEGATEQuando si impostamanualmente il delegatecon oggetti che lancianoeventi asincroni, come puòaccadere nel nostroUIWebView, si rischia diincorrere in chash casualise non si deallocanocorrettamente le risorseassociate se si rimuove ilcontrollore; tale eventoaccade frequentementequando si utilizza un controllerUINavigationController;conviene quindi rimuoverequesto legame nel metododealloc o appena possibile;

048-053:088-093-corsi-xsl 30-06-2009 16:13 Pagina 51

Page 21: 86746175 iPhone Programming

21 iPhone programming

iPhone programmingComponenti grafici e controllo UIWebView

il cui scopo è proprio quello di fornire una rispo-sta certa se si rispetta un determinato protocollo;tale metodo viene invocato automaticamentenumerose volte durante l'esecuzione del vostroapplicativo in maniera completamente traspa-rente. Dichiarare esplicitamente che una classe èconforme a uno, o più, protocolli permette sia disapere quali metodi sono obbligatori (nel casodei formali), perché anche una sola omissioneviene segnalata dal compilatore, ma soprattuttoè una pratica di buona programmazione (validasia per i formali che per gli informali), rendendopalesi le proprie intenzioni anche ad altri svilup-patori che potrebbero analizzare in futuro ilvostro codice. Per dichiarare se una classe èconforme a un protocollo, utilizziamo ad esem-pio quello mostrato precedentemente, UITableViewDataSource, è sufficiente inserire il suo iden-tificativo, racchiuso tra i due simboli minore emaggiore, all'interno del file di interfaccia dellaclasse, successivamente alla classe da cui derivala nostra:

@interface ioProgrammoArt2ViewController :

UIViewController <UITableViewDataSource>

Per aggiungere ulteriori protocolli basta sempli-cemente separarli con una virgola.UITable ViewDataSource è un protocollo chedichiara sia metodi obbligatori (due) che opzio-nal (numerosi), saremo quindi obbligati a imple-mentare solo i primi due, numberOfRowsInSection e cellForRowAtIndexPath. La frase con cui èstato terminato il paragrafo precedente è quindiparzialmente vera, è necessario dichiarare espli-citamente di essere conformi a uno o più proto-colli formali, ma è buona pratica farlo anche perquelli informali. È importante disaccoppiare ildelegate quando rimuoviamo le risorse associatea un UIViewController che contiene la nostraclasse, tale operazione si effettua semplicementeimpostandolo al valore nil:

miaIstanza.delegate = nil;

Si tratta di un'operazione necessaria, poiché, sevengono inviati messaggi al delegate quando que-sto non è più disponibile, in genere dopo esserestato deallocato, si verificherà un crash a runtime;un errore comune è quello che si presenta quandosi analizzano i dati dell'accelerometro e non sieffettua tale procedura di rilascio. Concludiamoquesto argomento suggerendo di porre una certaattenzione nella fase di scrittura della signature deimetodi, poiché una qualunque differenza con laversione attesa non permetterà a questi di essereinvocati quando necessario: la procedura più sicu-ra consiste nell'effettuare una operazione di copia,

prelevando le signature dalla documentazione delprotocollo, e incolla, all'interno di XCode.

IL CARICAMENTO DELLA PAGINANel nostro progetto decidiamo di monitorare ilcaricamento della pagina web mostrando, quan-do questo processo non è ancora terminato,un'animazione; per semplicità utilizziamo unUIActivityIndicatorView, un componente visualeche mostra un'animazione infinita (ma comun-que interrrompibile), il tipico effetto di una spi-rale rotante, familiare agli utenti Mac. Abilitiamo, tramite la finestra Attributes Inspector(CTRL+1) di IB, la voce Hide When Stopped, in talmodo nasconderemo automaticamente talecomponente quando interromperemo la sual'animazione. Per accedere nella nostra classeioProgrammoArt2ViewController.m a tale com-ponente visuale dobbiamo aggiungere, effet-tuando anche il collegamento in IB, un nuovoIBOutlet all'interno di ioProgrammoArt2View -Controller.h:

//file ioProgrammoArt2ViewController.h

#import <UIKit/UIKit.h>

@interface ioProgrammoArt2ViewController :

UIViewController {

IBOutlet UIWebView *webView;

IBOutlet UITextField *addressField;

IBOutlet UIButton *goButton;

IBOutlet UIActivityIndicatorView *loadMonitor; }

@end

Ricordate di associarlo in Interface Builder, altri-menti non si avrà modo di accedervi nei metodiche realizzeremo. Per mostrare l'utilità delladelegation, invece di realizzare una classe daassociare alla UIWebView, andremo a impostarecome delegate di questo componente visuale ilnostro UIViewController; come precisato prece-dentemente, è conveniente impostare il proto-collo, anche se non espressamente richiesto;consultando la documentazione veniamo aconoscenza che il suo delegate è del tipoid<UIWebViewDelegate>, dovremo quindi essereconformi al protocollo UIWebViewDelegate:

@protocol UIWebViewDelegate <NSObject>

@optional

- (BOOL)webView:(UIWebView *)webView

shouldStartLoadWithRequest:(NSURLRequest

*)request navigationType: (UIWebViewNavigation

Type)navigationType;

- (void)webViewDidStartLoad:(UIWebView *)webView;

- (void)webViewDidFinishLoad:(UIWebView *)webView;

MOBILE � Componenti grafici e controllo UIWebView

ht tp ://www. ioprogrammo. i t

G 52 /Agosto 2009

NOTA

IBACTIONIBAction viene utilizzato per

identificare un metodo diuna classe, in modo da

notificare Interface Builderche tale blocco di codicepotrà essere invocato daun componente grafico;

allo scaternarsi di uno o piùeventi consente, inoltre, di

esporre tale metodoall'interno di quelli forniti

da un determinato oggettovisuale di IB. Ciò avviene

quando si preme il destrosulla relativa riga neldocument inspector.

NOTA

DEASOCCIAREIBOUTLET

Ogni volta che si utilizza unIBOutlet, dichiarandoloall'interno del file .h e

associandolo ad uncomponente visivo tramite

Interface Builder, è necessario rilasciare la

memoria che gli vieneriservata a runtime; tale

operazione viene effettuatainvocando il metodo

release sul relativo IBOutletall'interno del metodo

dealloc.

048-053:088-093-corsi-xsl 30-06-2009 16:14 Pagina 52

Page 22: 86746175 iPhone Programming

iPhone programming 22

iPhone programming Componenti grafici e controllo UIWebView

ht tp ://www. ioprogrammo. i t

- (void)webView:(UIWebView *)webView

didFailLoadWithError:(NSError *)error;

@end

I metodi disponibili sono tutti opzionali, provve-diamo quindi a implementare solo webViewDidStartLoad, che viene lanciato quando ha ini-zio il caricamento di una pagina, e webViewDidFinishLoad che notifica il suo completamento.Prima dobbiamo impostare il delegate tramiteInterface Builder, operazione che come prece-dentemente spiegato, avviene trascinando ilrelativo cerchietto verso il File Owner,Ora dobbiamo passare a XCode e modificareprima il file ioProgrammoArt2ViewController.h

//file ioProgrammoArt2ViewController.h

#import <UIKit/UIKit.h>

@interface ioProgrammoArt2ViewController :

UIViewController < UIWebViewDelegate>{

IBOutlet UIWebView *webView;

IBOutlet UITextField *addressField;

IBOutlet UIButton *goButton;

IBOutlet UIActivityIndicatorView *loadMonitor;

}

@end

aggiungendo <UIWebViewDelegate>, e successi-vamente creando i due metodi richiesti, copian-

do la signature dalla documentazione, nel fileioProgrammoArt2ViewController.m

//file ioProgrammoArt2ViewController.m

#import "ioProgrammoArt2ViewController.h"

@implementation ioProgrammoArt2ViewController

-(IBAction) gotoAddress {

[webView loadRequest:[NSURLRequest requestWithURL

:[NSURLURLWithString:[addressField text]]]];

}

- (void)webViewDidStartLoad:(UIWebView *)webView {

// }

- (void)webViewDidFinishLoad:(UIWebView *)webView {

// }

@end

Basterà infine avviare l'animazione in un metodoe interromperla nell'altro: ciò è reso possibilenella classe UIActivityIndicatorView invocando idue metodi, privi di parametri, startAnimating estopAnimating.

- (void)webViewDidStartLoad:(UIWebView *)webView

{

[loadMonitor startAnimating];

}

- (void)webViewDidFinishLoad:(UIWebView *)webView

{

[loadMonitor stopAnimating];

}

CONCLUSIONITermina così questa serie di articoli in cui abbia-mo esposto alcuni dei concetti che creano mag-giori problemi quando si realizza per la primavolta un applicativo per iPhone. Nei prossimi mesimostreremo il funzionamento di altri componen-ti visuali, e forniremo ulteriori nozioni sul linguag-gio Objective-C. Buona programmazione.

Andrea Leganza

Agosto 2009/ 53 G

Componenti grafici e controllo UIWebView � MOBILE

Fig. 1: L’aggiunta del componente UIWebView alla nostra schermata

Fig. 2: Come si imposta il delegate

L’AUTORE

Laureato in IngegneriaInformatica, da oltre undecennio realizza soluzionimultimediali e non supiattaforme e con linguaggidiversi. Certificato AdobeACE - Adobe Flex 3 and AIRCertificatd Expert, EUCIPCore, appassionato difotografia, linguagiapponese e istruttore dinuoto FIN, è attualmenteimpegnato in numerosiprogetti multidediali conalcune società nazionali edinternazionali; ècontattabile [email protected] odirettamente sul sitowww.leganza.it

048-053:088-093-corsi-xsl 2-07-2009 17:54 Pagina 53

Page 23: 86746175 iPhone Programming

23 iPhone programming

iPhone programmingiPhone: gestire le tabelle per rappresentare i dati

REALIZZIAMO UNAAGENDA PER IPHONEDA QUESTO NUMERO INIZIA UNA TRATTAZIONE CHE È UN PO’ IL CUORE DI QUASI OGNIAPPLICAZIONE IPHONE. STIAMO PARLANDO DELLE TABELLE. VEDREMO COME SI CREANO,QUALI TIPOLOGIE UTILIZZARE A SECONDA DEL CONTESTO, E COME GESTIRLE AL MEGLIO

Uno dei componenti più utili e utiliz-zati nello sviluppo di applicativi su'iPhone, è quello identificato dalla

classe UITable View, una tabella a scrollingverticale, che consente di mostrare un elencoordinato, e opzionalmente modificabile daparte dell'utente, di informazioni generalmen-te coerenti. Tale componente è praticamenteonnipresente in ogni applicativo, è possibiletrovarlo in numerose personalizzazioni all'in-terno dell'elenco dei brani audio presenti nelvostro dispositivo, in quello dei film, in quellodella posta elettronica, fino all'applicativo deicontatti. Lo utilizzeremo prima nella versionefornita, creando un progetto omonimo utiliz-zando il wizard e provvederemo poi a custo-

mizzarlo progressivamente per realizzare unanostra versione graficamente più accattivante.Il progetto che andremo a realizzare sarà unatodo list, un normale elenco di azioni da fare,procedura che ci consentirà di analizzare indettaglio la struttura di tale componente visua-le, oltre che a prendere confidenza con le ope-razioni da effettuare per gestire l'interazionedell'utente e la relativa modifica, sia sottol'aspetto grafico, che semantico delle informa-zioni che andremo a presentare visivamente:tutto ciò ci darà modo di introdurre nuovi tipidi dato e di conoscere tecniche e classi forniteda Objective-C.

TABELLE CON UITABLEVIEWCome viene realizzata una tabella nella pro-grammazione iPhone, all'interno dell'SDK?La classe a cui fare riferimento è quella chiama-ta UITableView, le cui istanze ereditano a lorovolta dalla classe da cui questa discende diretta-mente: UIScrollView, tale componente consen-te di effettuare lo scrolling di contenuti, ed è uti-lizzato normalmente quando la dimensione di

CORSO MOBILE � iPhone: gestire le tabelle per rappresentare i dati

ht tp ://www. ioprogrammo. i t

G 48 /Settembre 2009

❑ CD ❑ WEBioProgrammoArt4.zip

cdrom.ioprogrammo.it

Conoscenze richiesteOOP

SoftwareMac OS X 10.5 osuperiore

Impegno

¥Tempo di realizzazione

REQUISITI

Fig. 1: L'interfaccia che realizzeremo al termine della serie di articoli incentrati sulla tabella Fig. 2: La catena di ereditarietà della classe UITable

048-053:088-093-corsi-xsl 28-07-2009 15:01 Pagina 48

Page 24: 86746175 iPhone Programming

iPhone programming 24

iPhone programming iPhone: gestire le tabelle per rappresentare i dati

ht tp ://www. ioprogrammo. i t Settembre 2009/ 49 G

iPhone: gestire le tabelle per rappresentare i dati � MOBILE

tali informazioni è superiore a quella concessadallo schermo dell'iPhone/iPod (320x480 pixelmassimo, ottenibili quando non si utilizzanocomponenti visuali di navigazione, comeUITabBarController e UINavigation Controller);risulta molto utile per mostrare a video immagi-ni di dimensioni relativamente grandi, oppureper fornire un semplice sistema di navigazionetra schede contenenti informazioni diverse; permostrare la posizione in cui ci troviamo, vengonoutilizzate due barre di colore normalmente nero,identiche a quelle utilizzate nel componente uti-lizzato nel precedente articolo, l'UIWebView.Rispetto a UIScrollView, UITableView aggiungealcuni metodi e variabili specifiche per consenti-re l'interazione da parte dell'utente e per otti-mizzare la visualizzazione e la persona-lizzazio-ne, sia estetica che semantica, delle singole righeche vengono utilizzate per visualizzare le infor-mazioni fornite; proprio le righe che abbiamoappena citato, in questo contesto prendono ilnome di celle (sono per tale motivo istanze dellaclasse UITableViewCell) e saranno oggetto princi-pale di questa serie di articoli; tratteremocomunque in un numero successivo della rivistaanche l'UIScrollView.Poiché la classe UITableView discende, attraversola classe padre, da UIView, ne eredita le caratteri-stiche, ed è necessario, per tale motivo, inserirequesto componente sempre all'interno di unController o di una classe da esso derivata; oltre aeffettuare tale pratica manualmente, utilizzandoun qualsiasi Controller, inserendo la UITableViewal suo interno, e gestendo la connessione tramitecodice Objective-C (IBOutlet), è possibile velo-cizzare il tutto utilizzando un componente rea-lizzato ad hoc, discendente di UIViewControllere chiamato UITableViewController. Tale oggettoha già al suo interno una variabile, una IBOutlet(tenete a mente le nozioni di IBOutlet e IBActionperché le ritroveremo sempre d'ora in poi,sarebbe opportuno andare a riconsultare gli arti-coli precedenti nel caso riteniate di non averlimetabolizzati) che ospiterà la nostra tabella e pertale motivo risulta la strada più veloce da seguirequando si utilizza Interface Builder, evitandoci lacreazione di una IBOutlet apposita. Inoltre,essendo alla sua radice una UIView, permette diessere personalizzata con componenti visivi,quali testi, immagini, bottoni etc. Se avete mododi analizzare con attenzione i diversi applicatividisponibili nativamente con l'iPhone /iPodTouch, troverete decine di varianti di questocomponente, per non parlare delle versioni rea-lizzate dai programmatori che pubblicanosull'Apple Store! È probabilmente il componente più personaliz-zato e utilizzato fornito dall'SDK.

LA CREAZIONE DEL PROGETTOPer semplificare questo tutorial, utilizzeremouna delle voci preesistenti presenti nel wizard deinuovi progetti; selezioneremo per tale motivo unnuovo progetto della categoria Navigation BasedApplication, che ha il pregio di essere già configu-rato con una UITableView, inserita all'interno diun UITableViewController.In questa circostanza non utilizzeremo la nuovafunzionalità introdotta con l'SDK 3, chiamataCore Data, e, nel caso la trovaste selezionata (èattivabile utilizzando il checkbox che apparequando si seleziona questo tipo di progetto, masolo se si ha aggiornato l'SDK alla versione 3)deselezionatela; per chi fosse curioso su cosa èCore Data, la risposta è semplice, è una nuova“funzionalità” che consente di gestire il Modelsenza dover scrivere comandi SQL per interfac-ciarsi con il database fornito dall'SDK: SQLITE:fornisce un livello di astrazione, che semplificanotevolmente l'utilizzo di questo database, espo-nendo metodi e metodologie il cui scopo è quel-lo di ridurre al minimo l'impegno necessario pergestire la memorizzazione delle informazioni,che possono essere non temporanee (il cuitempo di vita è quindi superiore al singolo utiliz-zo dell'applicativo) e/o quando queste sono alta-mente strutturate (si pensi alla memorizzazionedi informazioni di utenti se si realizzasse un siste-ma di interfacciamento con un social network):proprio per il loro particolare utilizzo dedicheremoin futuro opportuno spazio. Quando avremo completato la fase di creazionedel progetto troveremo alcuni file al suo interno,due xib (MainWindow.xib e RootViewController.xib) e alcune classi, tra le quali quella che ci inte-ressa prende il nome di RootViewController .h/.m.Cerchiamo ora di capire cosa è stato creato dalwizard, ma in particolar modo come è stato rea-lizzato.La procedura automatica ha creato un filexib che contiene (e conterrà) tutti i nostri com-ponenti visuali, una superview, chiamataMainWindow, al cui interno viene inserito auto-maticamente il componente chiamato RootViewcontroller.xib; questo file grafico è associato allaclasse omonima che è un'istanza della classeUITableViewController: aprendo tale file all'inter-no di Interface Builder verrà visualizzata unatabella perfettamente funzionante, basterà infat-ti eseguire il progetto nel simulatore per effettua-re lo scrolling utilizzando il dito. Anche se all'in-terno di Interface Builder, in questo caso, sonomostrati dei contenuti, delle città dellaCalifornia, non bisogna preoccuparsi di elimina-re tali voci, andandole a cercare all'interno delcodice del progetto, poiché queste sono sempli-cemente degli indicatori visivi di come potrebbe

NOTA

IPHONE 3GSIl nuovo iPhone 3GS montaun processore di circa600MHz, 256MB di RAM,OpenGL ES 2, contro ilprecedente di circa412MHz, 128MB di RAM eOpenGl ES 1.1, l'iPod Touch2G si pone nel mezzo concirca 533MHz e 128MB diRAM.

048-053:088-093-corsi-xsl 23-07-2009 11:50 Pagina 49

Page 25: 86746175 iPhone Programming

25 iPhone programming

iPhone programmingiPhone: gestire le tabelle per rappresentare i datiMOBILE � iPhone: gestire le tabelle per rappresentare i dati

ht tp ://www. ioprogrammo. i tG 50 /Settembre 2009

apparire la vostra tabella; non bisogna peròtenere molto in considerazione tale anteprima,poiché non rifletterà più la reale visualizzazionedella vostra tabella quando andrete a effettuareanche minimi cambiamenti al suo codice; se noneffettuerete invece personalizzazioni utilizzandoObjective-C, potrete effettuare modifiche utiliz-zando l'inspector (la finestra che contiene tuttele proprietà del componente visuale) per ognimodifica avrà un riscontro visivo immediato,anche se non sempre fedele. Avviare il progetto etestarlo, sia nel simulatore che sul dispositivo,sarà quindi l'unico modo per avere un riscontroreale dell'effettivo comportamento, e dell'aspet-to reale, della tabella. Purtroppo, nonostante larelativa semplicità del progetto, perderemo divista completamente Interface Builder, poichénon consente di effettuare modifiche estetiche(se non in maniera molto limitata) e strutturalialla tabella: utilizzeremo d'ora in poi solamentecodice Objective-C.

LE RAPPRESENTAZIONIDI UITABLEVIEWEsistono due tipi di rappresentazione grafica diuna tabella standard: quella chiamata plain, chemostra un elenco semplice di righe, che d'ora inpoi chiameremo con il nome utilizzato all'internodell'SDK e della documentazione ufficiale, ovverocelle, e quella chiamata groupe, in cui insiemi dicelle sono raggruppati in gruppi identificati da unastringa di testo. La prima modalità è la più sempli-ce, e si presta a essere utilizzata quando non sidesidera fornire una differenziazione tra diversetipologie di informazioni mostrate a schermo,come per un elenco di dati appartenenti a unastessa tipologia, utenti di uno stesso social networka esempio, mentre la seconda diventa la soluzionelogicamente, ma soprattutto graficamente, piùopportuna quando mostriamo a schermo diversetipologie di informazioni, elencando prodotti diun negozio ad esempio. La seconda modalità è uti-lizzata anche all'interno del menu settingsdell'iPhone, dove nella stessa schermata troviamo

valori e comandi utilizzati per configurare diversiapplicativi e impostazioni. La modalità grouped è inoltre la più adatta quandosi raggiunge l'ultimo livello di dettaglio delle infor-mazioni, quello in cui si mostrano tutti i datiriguardanti una determinata voce.Decidere quale delle due tipologie scegliere, èquindi un'operazione relativamente semplice,tenendo a mente la loro diversa funzione.

LA STRUTTURA DELLA UITABLEVIEW IN MODALITÀ GROUPEDLa modalità grouped merita un ulterioreapprofondimento, poiché presenta una strutturaparticolare, la cui personalizzazione consente divariare il suo aspetto di default.Nell'immagine di Fig.5 sono mostrati tre gruppicon realtive intestazioni, Remote Host: www.apple.

com, Access To Internet Hots e Access To LocalBonjour Hosts, ognuno di questi ha una sola cellaassociata: in questo esempio possiamo chiara-mente notare che viene creata una spaziaturasopra il gruppo chiamata padding (superiore),

Fig. 3: Come sono strutturati i componenti visuali

Fig. 4: La modalità di visualizzazione plain

Fig. 5: La modalità di visualizzazione grouped

IBACTIONIBAction viene

utilizzato per identificareun metodo di una classe, in

modo da notificareInterface Builder che tale

blocco di codice potràessere invocato da un

componente grafico; alloscaternarsi di uno o più

eventi consente, inoltre, diesporre tale metodo

all'interno di quelli fornitida un determinato oggetto

visuale di IB. Ciò avvienequando si preme il destro

sulla relativa riga neldocument inspector.

NOTA

048-053:088-093-corsi-xsl 23-07-2009 11:50 Pagina 50

Page 26: 86746175 iPhone Programming

iPhone programming 26

iPhone programming iPhone: gestire le tabelle per rappresentare i dati

ht tp ://www. ioprogrammo. i t Settembre 2009/ 51 G

iPhone: gestire le tabelle per rappresentare i dati � MOBILE

poi troviamo uno spazio utilizzato per contenetel'header, l'intestazione del gruppo, a cui segue lacella vera e propria con i suoi contenuti, seguitada un footer, un'area in cui è possibile inseriretesto ad esempio e infine da un ulteriore padding(inferiore). È possibile personalizzate tutte que-ste sezioni, ma solamente utilizzando codiceObjective-C, si inizia a comprendere comeInterface Builder perda di utilità anche nei con-testi più semplici rendendo necessario familia-rizzare con il linguaggio object oriented il piùvelocemente possibile.

CONSULTAZIONEMULTILIVELLOQuando si fornisce una lista di informazioni pre-sentate all'interno di una UITableView, general-mente si desidera rendere disponibile un sistemadi consultazione multilivello, dove viene mostra-to un primo livello, nel quale viene visualizzatoun elenco complessivo, che l'utente potrà scorre-re, e generalmente un secondo livello di accesso atali dati, nel quale si mostrano i dettagli relativialla cella selezionata nel livello precedente: è lostesso principio applicato nella navigazione dellepagine web utilizzando i menu. In realtà il nume-ro di tali livelli è virtualmente illimitato, ma supe-rare di numero il valore tre/quattro crea problemidi orientamento all'utente che utilizza tale siste-ma di navigazione, molti utilizzatori non sarannoin grado di ricordate percorsi superiori a tre inte-razioni e potrebbero trovare poco intuitivo taleapproccio: il numero di utilizzi di un'informa-zione presente in un determinato livello si riducein maniera inversamente proporzionale al nume-ro di tale livello: potreste scoprire con il tempoche, tanta fatica fatta per realizzare una determi-nata UIView, non è apprezzato poiché troppo inprofondità!

Come detto in precedenza, quando si presental'insieme di informazioni relative all'ultimo livel-lo, è consigliabile utilizzare una struttura di tipogrouped oppure, nel caso si desiderasse una strut-tura più complessa, una UIView (UIScroll View osimili) opportunamente strutturata.

GLI INDICATORI DI DETTAGLIOQuando si realizza una struttura multilivello ènecessario informare visivamente l'utente se esi-ste un successivo livello di navigazione cliccandosu una determinata cella oppure no. Per talemotivo sono disponibili due icone per rappre-sentare la disponibilità di un maggiore dettaglioper l'informazione selezionata, e che vengonoposizionate all'estrema destra delle singole celle. Una di queste, ottenibile impostando il tipo dicella come un UITableViewCellAccessory Disclosure-Indicator, e la cui immagine è una frecciaverso destra, simile al simbolo di maggiore dicolore grigio (>) chiamata Dislosure Indicator,informa che il prossimo livello generalmenteconterrà un'altra tabella (con maggiore detta-glio), e un'altra, UITableViewCellAccessory Detail-DisclosureButton, chiamata Detail Disclo sureButton, identificata da un cerchio celeste con lostesso simbolo di >, ma di colore bianco, che inquesto caso indica che il prossimo livello dovreb-be essere quello finale; nessun controllo automa-tico vieta di agire in maniera diversa, realizzandodiverse strutture con questi simboli, ma è alta-mente sconsigliato per evitare di non andarecontro le regole di usabilità dettate dalla ditta diCupertino che potrebbero impedire l'accet -tazione del vostro software per la venditasull'Apple Store (è capitato a numerosi utenti): ilconsiglio è quindi quello di essere fedeli al com-portamento standard quando si utilizzano sim-boli e i componenti forniti dall'SDK. Ovviamente l'assenza di uno o dell'altro simboloimplica la mancanza di alcuna schermata suc-cessiva alla presente, è per tale motivo dimostra-zione di scarsa attenzione non utilizzarli quando

NOTA

PLAIN E GROUPEDLa tabella di tipo plain sipresta per elenchi generici,mentre quella grouped perrappresentare leschermate di dettaglio,oppure quando leinformazioni mostrate sonoraggruppate per una o piùcaratteristiche comuni.

Fig. 6: Le diversa gestione degli spazi presente quando una tabella è del tipo grouped

Fig. 7: Un esempio di navigazione a tre livelli.Le informazioni sono sempre più dettagliatecon il progredire del numero di livello

048-053:088-093-corsi-xsl 23-07-2009 11:50 Pagina 51

Page 27: 86746175 iPhone Programming

27 iPhone programming

iPhone programmingiPhone: gestire le tabelle per rappresentare i dati

necessario. È inoltre disponibile una terza icona,generalmente utilizzata quando si effettuanoselezioni singole o multiple, quando si scelgono,ad esempio, diversi prodotti: è il simbolo dispunta (UITableViewCellAccessoryCheckmark)chiamato Check mark. Se questi sono i simbolipiù utilizzati, nessuno vieta, comunque, di per-sonalizzarli a proprio piacimento, inserendoimmagini create con il proprio programma diillustrazione preferita, renderete più accattivantee personale la vostra applicazione, evitando difarla sembrare troppo anonima e poco di impat-to; conviene comunque utilizzare simboli diimmediata comprensione per non confonderel'utente, evitando, ad esempio, di utilizzare frec-ce con verso illogico o immagini poco intuitive.

TIPOLOGIE DI CELLESDK 3.0 ha aggiunto un'utile proprietà per impo-stare il tipo di visualizzazione predefinita da uti-lizzare per le celle, nel caso non si volesse realiz-zare una propria versione. Precedentemente era necessario posizionare icomponenti visuali, utilizzando chiamateObjective-C, un'operazione che di ripeteva spessoin numerosi progetti, in questo modo si risparmiamolto tempo, e si evita di incorrere in problemi diallineamenti e trasparenze errati. Gli stili di visualizzazione sono i quattro seguenti:UITableViewCell StyleDefault, UITableViewCellStyleSubtitle, UITa bleViewCellStyleValue1,UITableViewCellStyleValue2. Il primo tipo consentel'inserimento di un'immagine sulla sinistra dellacella, un testo e un opzionale simbolo di dettaglio,il secondo aggiunge una didascalia sotto alla voceprincipale, mentre gli ultimi due sono solo elenchitestuali, non consentendo l'inserimento di alcunaimmagine. Il testo principale è accessibile tramitela proprietà textLabel, mentre quello più piccoloutilizzando quello detailTextLabel.

DATA SOURCE E DELEGATEOgni tabella, istanza di UITableView, ha bisogno diessere associata a due altre istanze, di qualunqueclasse; infatti, controllando l'API, noterete che èutilizzato in entrambi i casi l'identificatore id,sinonomio di “qualunque oggetto”; una istanzasarà responsabile di fornirle i dati da mostrare aschermo, e viene identificata dal Data Source(Model del pattern MVC), e un'altra provvederàa gestire aspetto e comportamento (View eController del pattern MVC): non è necessarioche queste due istanze siano diverse, infatti, inprogetti non molto strutturati, si preferisce farlicombaciare; tale comportamento viene inoltreseguito dal wizard stesso, che infatti ha provve-duto automaticamente ad associare le due pro-prietà alla nostra istanza di RootViewController:basterà andare in Interface Builder, premendo iltasto destro del mouse sul File Owner per verifi-care tale affermazione. Da parte sua laUITableView creata riveste il ruolo per ilRootViewController di View e di proprietàtableView. Si è realizzato quindi un ciclo didipendenze. UITableView interrogherà i duedelegati per effettuare tutte le operazioni, siaautomatiche che scatenate dall'interazione del-l'utente e su come presentare il proprio aspetto equello delle celle. Data Source utilizza il proto-collo UITableViewDataSource, mentre il delegatoil UITableViewDelegate; nel primo caso si richie-de al delegato, in questo caso il fileRootViewControl ler.h/.m, di essere interme- dia-rio tra la tabella e il model, fornendo su richiestale informazioni necessarie e consentendo il loroinserimento, modifica e/o cancellazione.UITableViewDe legate, invece, è responsabile difornire informazioni alla tabella su quale èl'aspetto delle celle e di come gestirel'interazione con l'utente, ed anche in questocaso coincide con RootViewController.h/.m; imetodi disponibili sono decine e la documenta-zione disponibile è la fonte più adatta.

IL PROCESSO DICREAZIONE DELLE CELLEIl procedimento necessario per la creazione ditutte le celle è relativamente semplice: la tabellainterroga il proprio delegato, UITableViewDataSource, richiedendo il numero di celle, sinonimodi righe, rows, utilizzando il metodo numberOfRowsInSection, per poi popolare le celle iterativa-mente invocando il metodo cellForRow AtIndexPath. Se non viene specificato, comeavviene in questo esempio il numero di gruppidisponibili, identificati dal termine sections, e

MOBILE � iPhone: gestire le tabelle per rappresentare i dati

ht tp ://www. ioprogrammo. i t

G 52 /Settembre 2009

Fig. 8: I quattro tipi predefiniti di celle disponibili dalla versione 3.0 dell'SDK

NOTA

IL SISTEMA DI NAVIGAZIONE

MULTILIVELLOQualunque utilizzo di un

applicativo iPhone èpensato o come lo sfogliare

un libro, muovendosiparallelamente, oppure

come navigare all'internodi un sito web, ottenendoinformazioni sempre più

dettagliate man mano chesi scende in profondità

048-053:088-093-corsi-xsl 23-07-2009 11:50 Pagina 52

Page 28: 86746175 iPhone Programming

iPhone programming 28

iPhone programming iPhone: gestire le tabelle per rappresentare i dati

ht tp ://www. ioprogrammo. i t

ottenuto dalla tabella invocando sul delegate ilmetodo opzionale sectionIndexTitlesForTableView,tale valore è assunto come unitario e non verran-no visualizzati header/footer e relative spaziatu-re. Il metodo cellForRowAtIndexPath svolgequindi tutto il lavoro necessario per la generazio-ne del componente visuale che verrà utilizzatoper la tabella:

- (UITableViewCell*) tableView:(UITableView*)

tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

riceve per tale motivo come parametri la tabella,e le coordinate della cella, contenuti nella varia-bile indexPath, che nella sua versione più sem-plice fornisce solo la riga richiesta, nel caso di unsemplice array monodimensionale, ma quandosi utilizzano strutture dati più ramificate, questaè caratterizzata da numerosi indici (ad esempio1.4.5), come avviene per i paragrafi di un libro,allo scopo di identificare univocamente la posi-zione delle informazioni richieste. Tale metodo crea il componente visuale,un'istanza della classe UITableViewCell , figlia diUIView, configurandola attraverso l'inserimentodi oggetti visuali, immagini e testi inclusi, richie-dendo le informazioni al model (array o altrastruttura dati): è qui che generalmente vienescritta la maggior parte del codice, anche se, direcente, con l'aggiunta dei tipi di celle predefini-ti, fornita dall'SDK 3, si è notevolmente ridotta.

MIGLIORARE LE PERFORMANCEQuando effettuate lo scrolling della tabella con unnumero superiore a qualche decina, noterete ini-

zialmente una fase di rallentamento iniziale, chesuccessivamente non si presenterà. Tale comportamento è dovuto alla necessità daparte del sistema di effettuare caching delle cellemostrate.Nel caso andaste a visualizzare decine di cellein una sola schermata, potrete avere comun-que fenomeni di rallentamenti, enfatizzati seavete personalizzato le vostre celle, aggiungen-do immagini, testi, pulsanti, o altre UIView;ciò dipende da come gestirete i contenuti pre-senti all'interno della singola cella; preferitequindi contenuti senza trasparenza, ottenutiimpostando la proprietà di opacità tramiteObjective-C utilizzando nomevariabile.opaque= YES o tramite IB selezionando il checkboxOpaque (quando possibile). Tale accorgimento consente di migliorare,spesso in maniera evidente, il rendimento avideo durante la fase di scrolling: ridurre alminimo gli oggetti visuali trasparenti è quindiuna buona operazione che non dovrebbe esse-re effettuata nella fase finale dello sviluppo;inserire, come è stato detto, un certo numerodi oggetti, pulsanti, campi di testo e/o immagi-ni, in una cella ne degrada le performance: intal caso una soluzione consiste nel rasterizza-re, convertendo quindi in immagine, l'interocontenuto della cella che si vuole mostrare aschermo, e inserire solamente questo all'inter-no della stessa; in questo modo si migliorerànotevolmente la prestazione complessiva delvostro software. Un ulteriore accorgimento può essere quello dinon creare una tabella per ogni livello di detta-glio, ma di modificare ogni volta sempre lastessa, in modo da ridurre al minimo il dispen-dio di risorse. Il nuovo iPhone 3GS, ha al suointerno un processore con circa 200 MegaHertzin più rispetto alla precedente versione, ciòsignifica un incremento effettivo di prestazionicomunque evidente, con relativo aumento divelocità di risposta nelle più comuni operazio-ni, testare quindi il vostro gioco o applicativosolo su tale dispositivo potrebbe essere ungrande errore, poiché il numero di iPhone3G/2G è al momento maggiore rispetto a quel-lo di esemplari del nuovo modello: cercate ditestare anche su uno dei due precedenti, incaso non risultasse notevolmente lento anchesu questi dispositivi ne trarrete sicuramentevantaggio, sia di feedback positivi ottenutidagli acquirenti, che di download, e in caso divendita, di guadagni, perché ovviamente rag-giungerete un numero più cospicuo di utentiinteressati al vostro progetto..

Andrea Leganza

Settembre 2009/ 53 G

iPhone: gestire le tabelle per rappresentare i dati � MOBILE

Fig. 9: La tabella richiede il numero di righe e il numerodi sezioni che la stessa deve ospitare

Fig. 10: La Tabella utilizza il file RootViewControllercome delegate per entrambi i protocolli

L’AUTORE

Laureato in IngegneriaInformatica, da oltre undecennio realizza soluzionimultimediali, e non, supiattaforme e con linguaggidiversi. Certificato AdobeACE - Adobe Flex 3 and AIRCertificatd Expert, EUCIPCore, appassionato difotografia, linguagiapponese e istruttore dinuoto FIN, è attualmenteimpegnato in numerosiprogetti multidediali conalcune società nazionali ed internazionali; è contattabile [email protected] odirettamente sul sitowww.leganza.it

048-053:088-093-corsi-xsl 23-07-2009 11:50 Pagina 53

Page 29: 86746175 iPhone Programming

29 iPhone programming

iPhone programmingGestire la memoria nelle applicazioni iPhone

IPHONE: GESTIONEDELLA MEMORIAPER CHI SI ACCINGE A SVILUPPARE APPLICAZIONI PER LO SMARTPHONE DI CASA APPLE, UNO DEGLI ARGOMENTI SICURAMENTE PIÙ OSTICI, È LA GESTIONE DELLA MEMORIA.IN QUESTO ARTICOLO AFFRONTEREMO PROPRIO QUESTA IMPORTANTE TEMATICA

Nell'articolo precedente abbiamo descritto lastruttura, intesa come insieme di componentivisuali, che viene creata automaticamente

quando realizziamo, attraverso l'utilizzo nel wizard, unprogetto del tipo Navigation-Based Application; èstato inoltre introdotto il componente chiamatoUITableView e le relazioni che si instaurano tra lapopolazione delle celle (sinonimo di righe dellatabella) e il metodo cellForRowAtIndexPath, che,come spiegato, svolge tutto il lavoro necessarioper la generazione di tali componenti visualiall'interno del delegate. Analizzeremo tale meto-do in maniera dettagliata, in modo da introdurrealcune caratteristiche del linguaggio Objective-Cche, potremo affermare senza alcun dubbio, esse-re le più importanti, e anche più impegnative dacomprendere: per tale motivo è vivamente consi-gliato testare personalmente i vari aspetti trattatiin queste pagine per metabolizzarli correttamen-te.

NIL, NIL, NULL E NSNULLOgni istanza di un oggetto, quando viene definita (ilsuo stato interno non è quindi ancora stato inizia-lizzato) assume automaticamente valore nil (adesclusione di isa che assume la struttura della classedi cui la variabile è istanza); in ambito Objective-Cnil indica che una variabile (che dovrebbe puntare aun'istanza di un oggetto) non punta ad alcuna loca-zione di memoria. NULL, familiare a chi program-ma in C, è anche disponibile nel linguaggioObjective-C, ovviamente questo è dovuto al fattoche tale linguaggio è una versione opportunamentemodifica del C. nil viene quindi utilizzato per i pun-tatori ad oggetti, mentre NULL per qualunque pun-tatore (esistono ad esempio quelli a funzioni);entrambi, comunque, se andiamo ad analizzare laloro effettiva semantica, assumono valore pari a(void *)0.

#ifndef NULL

#define NULL __DARWIN_NULL

#endif /* ! NULL */

#ifndef nil

#define nil NULL

#endif

Se utilizzerete sempre Objective-C incontreretemolto raramente NULL, a meno di accedere adalcune funzionalità particolari, che generalmentescendono a livelli più bassi, in linguaggio C puro,quindi, è comunque necessario essere a conoscen-za della differenza tra i due e dei diversi contesti incui li potete incontrare. Nil, con la prima letteramaiuscola, è utilizzato per i puntatori a classi, e pertale motivo lo troverete ancora più raramente, poi-ché generalmente viene utilizzato lo stesso nil(sono anche questi sinonimi dal punto di vista sin-tattico). NSNULL è presente invece in quelle classi chefanno parte della Foundation collection (NSArray,NSSet, NSDictionary e altre) poiché queste nonpossono contenere il valore nil . Se vogliamo veri-ficare che un oggetto è appena stato creato, manon ancora inizializzato, possiamo effettuare ilseguente controllo:

if (myObject==nil)

{

//inizializzazione

...

}

Sarebbe ideale effettuare tale controllo prima diogni utilizzo di una variabile, ma risulterebbedispendioso sia in termini di memoria/risorse siaper numero di righe di codice, conviene quindi uti-lizzarlo in quelle situazioni in cui si è dubbiosisullo stato di una variabile, ad esempio quandoquesta viene creata in un metodo e utilizzata in unaltro, e spiegheremo a breve perché tale tecnicapuò risultare di fondamentale utilità. Inoltre, invocare qualunque metodo su tale varia-bile restituisce sempre 0 (in realtà quasi sempre,ma non ci addentriamo troppo) e viene quindi

CORSO MOBILE � Gestire la memoria nelle applicazioni iPhone

ht tp ://www. ioprogrammo. i t

G 48 /Ottobre 2009

❑ CD ❑ WEBioProgrammoArt4.zip

cdrom.ioprogrammo.it

Conoscenze richiesteOOP

SoftwareMacOS X 10.5.4 osuperiore, XCode

Impegno

¥Tempo di realizzazione

REQUISITI

048-053 def:088-093-corsi-xsl 31-08-2009 18:07 Pagina 48

Page 30: 86746175 iPhone Programming

iPhone programming 30

iPhone programming Gestire la memoria nelle applicazioni iPhone

ht tp ://www. ioprogrammo. i t Ottobre 2009/ 49 G

Gestire la memoria nelle applicazioni iPhone � MOBILE

interpretato come false nei controlli condizionali,if, else, while etc. A differenza del linguaggio C, in cui invocare suuna variabile NULL generalmente causa un crashdell'applicativo, in questo contesto si ottiene inve-ce sempre un risultato da tali chiamate, anche sepoi inutilizzabili nella pratica, oltre che sintomo discarsa comprensione del funzionamento del lin-guaggio, e causa di dispendio di risorse, anche semodestissimo, ma che in un dispositivo con risor-se relativamente limitate può fare la differenza,dovuto allo scatenarsi di tutto il sistema di invoca-zione del metodo relativo (anche se inesistente); èquindi perfettamente lecito effettuare la seguentechiamata:

NSString miaStringa;

miaStringa = nil;

[miaStringa isEqualTo:altraStringa];

Spesso, proprio utilizzare una variabile che assu-me il valore nil come parametro di un metodo, sca-tena una serie di problematiche che generalmenteterminano con un crash del sistema: sono la causapiù frequente di problemi quando si programmacon tale linguaggio e per evidenziare tali situazioniè necessario generalmente utilizzare il debuggerfornito, che, come scopriremo in un prossimo arti-colo, è “semplicemente” il famoso gdb, provenien-te direttamente dall'ambiente GNU/Linux (manon solo). È possibile assegnare in qualunque momento nilquale valore di una variabile, ma in tal caso c'è ilrischio di non rilasciare le opportune risorse asso-ciate a tale istanza (situazione identificata con iltermine leak),

//effettuare prima la deallocazione delle risorse

associate alla variabile myObject.

myObject==nil

//qualunque chiamata successiva non avrà alcun

risultato

//Non effetturare tale operazione se la variabile

non è stata deallocata opportunamente!!!

...

}

le motivazioni di tale affermazione verranno spie-gate successivamente, quando si introdurranno iconcetti relativi alla gestione della memoria. Per concludere questa sezione è opportuno ricor-dare che esistono altri valori che assumono valore0, ma utilizzati in contesti prettamente grafici:NSZeroPoint (un punto di coordinate 0,0).NSZeroSize (una dimensione NSSize di larghezza elunghezza 0) e NSZeroRect (un rettangolo NSRectposizionato nell'origine di di larghezza e lun-ghezza 0);

IL METODOCELLFORROWATINDEXPATHDopo aver descritto cos'è nil troverete sicura-mente più semplice la lettura del codice delmetodo cellForRowAtIndexPath, che qui perchiarezza presentiamo nella versione prodottadirettamente dal wizard:

- (UITableViewCell *)tableView:(UITableView

*)tableView cellForRowAtIndexPath:

(NSIndexPath *)indexPath {

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView

dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {

cell = [[[UITableViewCell alloc]

initWithStyle:UITableViewCellStyleDefault

reuseIdentifier:CellIdentifier] autorelease];

}

// Configurazione della cella

return cell;

}

Generalmente, una tabella ottiene i dati da unastruttura dati, un array ad esempio, in ambienteObjective-C identificato dalla classe NSArray, edovrà mostrare, a meno di particolari condizioni,come un semplice filtraggio effettuato in unaricerca, tutti gli oggetti in essa contenuti e utiliz-zare i valori di ogni singola istanza per popolarele celle della tabella: se ogni oggetto conterrà alsuo interno una stringa, potremo utilizzare talevariabile per mostrare nella cella un testo visibileall'utente che scrollerà la tabella. Quando la tabella, come è stato spiegatoapprofonditamente nell'articolo precedente,richiede al proprio delegate la struttura delle sin-gole celle, operazione che avviene invocando ilmetodo suddetto, effettua sempre alcune opera-zioni, obbligatorie, e presenti nel metodocellForRowAtIndexPath prodotto dal wizard:viene creato un certo numero di celle (grafica-mente parlando), che non corrisponde al realenumero di oggetti presenti nella struttura dati:quando verrà effettuato lo scrolling della tabellatali celle verranno riutilizzate e ne verranno cam-biati i valori interni, nel nostro caso, quindi,(ricordiamo che stiamo progettando un’appli -cazione agenda), l'azione da effettuare per unacerta giornata: questo procedimento è statoideato per ottenere un notevole risparmio dirisorse, si pensi che cosa succederebbe se sicreassero tutte le istanze delle celle anche quan-do l'utente non le visualizza: quasi ogni applica-tivo verrebbe chiusa per saturazione dellamemoria disponibile nel dispositivo. Con questatecnica viene risolto questo possibile collo di

048-053 def:088-093-corsi-xsl 31-08-2009 18:07 Pagina 49

Page 31: 86746175 iPhone Programming

31 iPhone programming

iPhone programmingGestire la memoria nelle applicazioni iPhoneMOBILE � Gestire la memoria nelle applicazioni iPhone

ht tp ://www. ioprogrammo. i tG 50 /Ottobre 2009

bottiglia in maniera molto intelligente; quelladescritta non è comunque una tecnica presentesolo in tale linguaggio: prende il nome di UIVirtualization, ed è possibile ritrovarla, ad esem-pio, anche in Adobe Flex 4 per il nuovo compo-nente DataGroup (Spark), e in WindowsPresentation Foundation per il componenteDataGrid fornito nel WPF Toolkit. La variabile sta-tica cellIdentifier viene creata solamente durantela prima invocazione del metodo e permette diidentificare la tipologia di cella che stiamo crean-do, utilizzando una stringa, che in questo casocorrisponde al valore “Cell”; nessuno vieta di crea-re diverse celle con diverse caratteristiche e deci-dere, a seconda delle necessità, quale utilizzareper un determinato oggetto prelevato dalla nostrastruttura dati. Se volessimo quindi avere due celle diverse perqualche caratteristica, basterebbe effettuare laseguente operazione:

static NSString * CellIdentifierWhiteBackground =

@"Cell";

static NSString *CellIdentifierBlackBackground =

@"CellBlackBackground";

if (decisioneCellaNormale)

UITableViewCell *cell =

[tableView dequeueReusableCellWithIdentifier:CellI

dentifierWhiteBackground];

{

//codice di formattazione grafica e

popolazione della cella

}

else //decisioneCellaBlackBackground

UITableViewCell *cell = [tableView dequeueReusable

CellWithIdentifier:CellIdentifierBlackBackground];

{

//codice di formattazione grafica e

popolazione della cella

}

return cell;

Potremo inoltre conoscere il tipo di cella richia-mando la proprietà (di cui parleremo successi-vamente in maniera approfondita, per ora iden-tificatela come una variabile pubblica, accessi-bile quindi dall'esterno della classe) current-Cell.reuseIdentifier che restituirà la stringa uti-lizzata (nel nostro esempio corrispondente a Cel-lWhiteBackground oppure CellBackBackground).Proseguendo notiamo che prima viene verifica-to che non esiste già una cella con l'identificatoreche abbiamo deciso (“Cell”) , in caso non sia sta-ta già creata viene invocato il costruttore dellacella, utilizzando lo stile UITableViewCellStyle-Default (spiegato nell'articolo precedente) e ta-le istanza viene inserita nella Autorelease Pool.

LE REGOLE PER LAGESTIONE DEGLI OGGETTIQueste regole sono i concetti fondamentali chedevono essere sempre tenuti in considerazionedurante lo sviluppo di qualunque applicativo, ven-gono introdotti solo ora perché negli articoli prece-denti non erano utilizzati quegli aspetti del lin-guaggio che potevano indurre confusione e gene-rare crash, inoltre si è alleggerito il contenuto delprimo tutorial :

� Un oggetto può essere utilizzato da uno o più proprietari (altri oggetti);

� quando un oggetto non ha proprietari, viene distrutto automaticamente senza alcun preavviso e in un momento imprecisato durante l'esecuzione del vostro applicativo dall'Autorelease Pool, generalmente dopo l'uscita dal metodo in cui è stato creato;

� per impedire la sua distruzione automatica si deve notificare l'Autorelease Pool che si è divenuti proprietari di tale oggetto (utilizzandoil metodo retain);

� se non si è più interessati a tale oggetto, si deve smettere di esserne proprietari (utilizzando i metodi release/autorelease) per consentirne una possibile distruzione da parte dell'Autorelease Pool;

� il numero delle dichiarazioni di appartenenza (retain) deve essere bilanciato con quello dei ri-lasci (release) in modo da consentire il rilascio automatico;

� il numero di dichiarazioni di utilizzo viene chiamato retainCount e assume superiori o uguali a 0, in caso assuma quest'ultimo verrà deallocato dall'Autorelease Pool;

� quando si raggiunge una certa esperienza è opportuno utilizzare il più possibile alloc/retain/release invece di utilizzare autorelease, per motivi prestazionali, soprattutto all'inter-no di cicli con un numero consistente di ite-razioni;

� il sistema invia un avviso di risorse insufficien-ti (principalmente RAM) quando questa decre-sce raggiungendo un valore prossimo ad 1.5MB.

Se la prima regola è un concetto condiviso datutti i linguaggi Object-Oriented (e non solo),ed è quella che consente di condividere unavariabile tra più istanze di una o più classi, ana-lizzando le altre si può intuire che esistono duemetodologie per gestire la memoria occupatada una variabile, una automatica (AutoreleasePool) e una manuale (alloc/retain/release) doveè responsabilità del programmatore gestire iltutto; non sempre la modalità automaticarisponde alle esigenze di sviluppo e per talemotivo entra in gioco la seconda.

ULTERIORIAPPROFONDIMENTIPer approfondire i concettimostrati in questo articolo,

consultare la guidachiamata Memory

Management ProgrammingGuide for Cocoa disponibile

su sitodeveloper.apple.com o

all'interno delladocumentazione fornita con

Xcode

NOTA

048-053 def:088-093-corsi-xsl 31-08-2009 18:07 Pagina 50

Page 32: 86746175 iPhone Programming

iPhone programming 32

iPhone programming Gestire la memoria nelle applicazioni iPhone

ht tp ://www. ioprogrammo. i t Ottobre 2009/ 51 G

Gestire la memoria nelle applicazioni iPhone � MOBILE

AUTORELEASE POOLL'Autorelease Pool nasce come necessità, inambiente Objective-C di fornire un sistema auto-matico di rilascio delle risorse non utilizzate, chemolti potrebbero identificare con il sistema diGarbage Collection (GC) presente in numeroseVM, Java e .NET in primis; poiché nel dispositivoApple non viene eseguito un vero motore di GC,principalmente per motivi prestazionali e dioccupazione di risorse, quando avviamo i nostriapplicativi (ricordo che alla fine sono scritti inlinguaggio C) questa è la soluzione automatizza-ta che ci viene fornita. Come funziona l'Autorelease Pool? Tale compo-nente è identificabile come un analizzatore diistanze di oggetti sulle quali è stato invocato ilmetodo autorelease, sono stati quindi identifica-ti come oggetti il cui ciclo di vita viene decisodall'Autorelese Pool stesso; questo provvede aliberare la memoria, distruggere quelle istanze dioggetti non più necessari, in maniera automati-ca, analizzando una particolare proprietà chia-mata retainCount (un variabile numerica pre-sente in ogni istanza il cui funzionamento verràspiegato a breve): quando troverà questa pari alvalore 0. L'autorelease pool invoca sulla variabileun metodo, che viene identificato come il suodistruttore, chiamato dealloc, nel quale si effet-tuano generalmente operazioni di rimozione dilegami con altre variabili (utilizzando il comandorelease/dealloc spiegati in seguito). Durante il ciclo di vita di una variabile, ilretainCount viene quindi incrementato e decre-mentato, fino generalmente a terminare in undeterminato istante t, in cui questo assumeràvalore 0: non è detto che si raggiunga tale valoredurante l'utilizzo del software, è ammissibile cheuna variabile abbia retainCount superiore a 0 pertutta la sua esistenza, e termini la sua vita allachiusura dell'applicativo. L'operazione di rilascio automatico avviene inmaniera non prevedibile e non è possibile quin-di sapere quando viene effettuata in maniera

automatica, a meno di notificare tale evento uti-lizzando le stampe a schermo all'interno delmetodo dealloc, generato automaticamente inogni classe da parte del wizard; questa operazio-ne è realizzabile semplicemente aggiungendouna stampa a schermo prima dell'invocazionedel metodo distruttore sulla classe padre:

-(void) dealloc {

NSLog(@”Istanza deallocata”);

[super dealloc];

}

In questo modo, osservando la finestra di logquando si avvia un applicativo in modalità didebug, si avrà modo di sapere con una certa pre-cisione quando un'istanza viene deallocata. Spesso, quindi, una variabile impostata perl'autorilascio (autorelease) che si utilizzava senzaproblemi durante l'esecuzione del proprio appli-cativo, in numerosi metodi nei quali si presuppo-neva, erroneamente, che non sia stata mai deal-locata, a un successiva esecuzione dello stessopotrebbe causare in crash il sistema dopo alcunisecondi, o anche minuti; cosa è accaduto? Nelprimo caso il pool non ha effettuato puliziadurante l'utilizzo, mentre nel secondo caso haprovveduto a deallocarla automaticamente. Quando invochiamo autorelease su un oggettorichiediamo il decremento del suo retainCountdi un'unità in un momento futuro, se questoscenderà a 0 ci sarà il conseguente rilascio auto-matico. Se andiamo ad analizzare il contenutodel file main.m, generato automaticamente inogni progetto dal wizard, troverete il seguentecodice:

NSAutoreleasePool * pool = [[NSAutoreleasePool

alloc] init];

int retVal = UIApplicationMain(argc, argv, nil, nil);

[pool release];

La prima riga crea e inizializza l'autoreleasePool, che identifichiamo come Main AutoreleasePool, cioè autorelease pool principale, successi-vamente, nella seconda riga, viene avviato ilnostro applicativo , e nella terza viene invocato ilmetodo release su tale pool.Tutto ciò che è contenuto tra la riga di creazione delpool e la chiamata del metodo release viene gesti-to da tale oggetto. È possibile creare uno o più re-lease pool al di fuori del Main Autorelease Poolper rispondere a determinate esigenze? Certo, an-che se i contesti in cui fatta questa scelta sono par-ticolari, generalmente comunque ci si affida al Au-torelease Pool principale, l'utilizzo più frequenterealizzato in maniera manuale da un program-matore, è quello di realizzarne una versione ad

Fig. 1: L'Autorelease pool distrugge le istanze delleclassi con retainCount pari a 0 automaticamente, invo-cando su di esse il metodo dealloc()

048-053 def:088-093-corsi-xsl 31-08-2009 18:07 Pagina 51

Page 33: 86746175 iPhone Programming

33 iPhone programming

iPhone programmingGestire la memoria nelle applicazioni iPhoneMOBILE � Gestire la memoria nelle applicazioni iPhone

ht tp ://www. ioprogrammo. i t

G 52 /Ottobre 2009

NOTA

RIFERIMENTI WEBCreazione dell'account, per

scaricare l'SDKgratuitamente e consultare

la documentazione:http://developer.

apple.com/iphone/

hoc all'interno di un ciclo nel quale vengono generatedecine, se non centinaia o addirittura migliaia, divariabili temporanee ma viene sconsigliato in am-biente iPhone perché introduce un sovraccaricocomputazionale che in alcune situazioni potreb-be obbligare il sistema a terminare la vostra ap-plicazione.

Un utilizzo generalmente più comune è quando siutilizzano i thread e si rende necessario creare una poollocale a tale ambiente di esecuzione. L'operazione dipulizia viene generalmente forzata quando le risor-se di sistema scendono sotto alcuni limiti, se si veri-ficano tali condizioni inoltre viene invocato per ogniUIViewController (e relative classi) il metodo didRe-ceiveMemoryWarning, nel quale si dovrebbe prov-vedere alla rimozione manuale di quelle istanze nonstrettamente necessarie e non gestite dall'Atorelea-se Pool. È obbligatorio invocare il metodo autore-lease su una variabile per affidarla alla gestione del-l'Autorelease Pool? No, tale operazione viene effettuataogni volta che viene creata una variabile senza utilizzareil metodo alloc;

-(void) inizializzazione {

NSString *newText = [NSString stringWithFormat

@"Lavoro da fare."];

//autorelease automatico, retainCount scenderà da 1

a 0 in un non precisato istante dopo la fine del

metodo;

//area di utilizzo dellavariabile

}

//al tempo t (random) dopo l'uscita dal metodo verrà

deallocata automaticamente

Questo tipo di inizializzazione, fornito da NSString,utilizando uno dei suoi cosiddetto Factory Methods(metodi statici che generano su richiesta istanzedella classe), permette di utilizzare la variabileall'interno del metodo in cui questa viene creata everrà rilasciata, in un non precisato momento,quando tale metodo terminerà, ciò avviene perchéquando inizializziamo newText questa assumeretainCount 1 e, alla prossima analisi da partedell'Autorelease Pool, verrà deallocata poiché,essendo autorelease, subirà un decremento auto-matico di tale valore di 1. Questo utilizzo general-mente non causa problemi, perché la deallocazio-ne automatica da parte dell'autorelease Pool avvie-ne sempre tra una chiamata e l'altra di un metodo;è un approccio comunemente utilizzato quando siutilizzano cicli (while e for ad esempio) in cui sicreano numerose variabili, ma si desidera che ven-gano rimosse automaticamente quando non piùnecessarie (in alcune situazioni non è la soluzionemigliore in termini prestazionali, quindi è necessa-rio utilizzare la modalità “manuale” adoperandoquindi alloc/retain/release. Se definissimo unavariabile esternamente al metodo, per poi inizializ-zarla al suo interno per un utilizzo in uno o altrimetodi in istanti successivi, verrebbe gestita talesituazione dall'Autorelease Pool?

NSString *newText;

-(void)inizializzazione {

newText = [NSString stringWithFormat

@"Lavoro da fare."];

}

-(void)mostraVariabile{

NSLog(@”%@”,newText);

}

Invochiamo il metodo inizializzazione per inizia-lizzare la nostra variabile e poi, invochiamo in altrimetodi quello chiamato mostraVariabile, notere-mo che casualmente, otterremo dei crash del siste-ma: ciò è dovuto al fatto che l'autorelease Pool harilasciato la variabile tra due successive chiamatedi mostravariabile (Fig.3): ecco uno dei motivi piùfrequenti di crash della applicazioni su iPhone:assumere che una variabile sia sempre disponibilequando creata! Le regola, la ribadiamo, è laseguente: “se nella creazione della variabile nonviene usato alloc oppure retain, come vedremosuccessivamente, tale variabile verrà rimossa inmaniera casuale e senza alcuna prevedibilità”,memorizzate bene questo concetto perché altri-menti passerete intere giornate cercando di capireperché il vostro applicativo crasha casualmente.

Autorelease Pool provvede inmaniera automatica etrasparente a rilasciare lamemoria allocata per un oggettoin un non precisato momentodurante l'esecuzione del vostroapplicativo. Ogni istanza su cui

non viene invocato il metodoalloc (ma anche , allocWithZone:,copy, copyWithZone:,mutableCopy,mutableCopyWithZone o retain,viene gestita da tale oggettoautomaticamente.

AUTORELEASE POOL

Fig. 2: Un esempio fornito dalla stessa Apple sul funzionamento dell'Autorelease Pool

048-053 def:088-093-corsi-xsl 31-08-2009 18:07 Pagina 52

Page 34: 86746175 iPhone Programming

iPhone programming 34

iPhone programming Gestire la memoria nelle applicazioni iPhone

ht tp ://www. ioprogrammo. i t

Come evitare questo problema di autorilascio? Unprimo modo è quello di utilizzare retain, in mododa notificare all'Autorelease Pool che vogliamo uti-lizzare tale variabile e che siamo in prima personaresponsabili del suo rilascio, siamo quindi diventa-ti quindi uno dei co-proprietari di tale variabile; ilsecondo consiste nel creare una variabile utiliz-zando alloc, metodo che spiegheremo nel prossi-mo articolo. Questo problema di accesso, dovuto auna scarsa comprensione della gestione in questocontesto, è probabilmente uno dei motivi più fre-quenti di crash quando si programma per i dispo-sitivi di casa Apple.

SE SI ESAURISCONO LERISORSE DEL DISPOSITIVONonostante Apple abbia cercato di fornire al singo-lo applicativo che viene eseguito volontariamentedall'utente (sono ovviamente in esecuzione pro-cessi di supporto in maniera trasparente, comequelli di localizzazione, accelerometri e altri senso-ri, gestione della di telefonia etc) la massima quan-tità di memoria disponibile, è sempre possibileincorrere nella situazione in cui si occupi una talequantità di memoria durante l'esecuzione del pro-prio software, sia a causa dell'utilizzo di grandistrutture dati, sia per errori di programmazione, chegenerano i cosiddetti leak (locazioni di memoriaoccupate, ma non rilasciabili dall'Autorelease Poole neppure dall'utente, di cui parleremo nel prossi-mo articolo), raggiungendo un valore prossimo a

1.5MB, scatenando un sistema di avvisi e invocazio-ni di metodi automatici; per ogni istanza figlia diret-ta, o indiretta, di UIViewController verrà invocato:

- (void)didReceiveMemoryWarning {

NSLog(@"Memoria insufficiente ricevuto nel

UIViewController!!!!");

}

mentre per il file chiamato nome_applicativo App-Delegate.m, quello responsabile della visualizzazio-ne e della gestione del nostro applicativo :

- (void) applicationDidReceiveMemoryWarning:

(UIApplication*)application {

NSLog(@"Memoria insufficiente!!!!");

}

Verrà inoltre inviata al nostro applicativo una notifica(argomento non trattato in questa serie di articoli)del tipo UIApplicationDidReceiveMemoryWarnin-gNotification. In ognuno di questi metodi dovremoprovvedere in maniera manuale alla rimozione dirisorse non necessarie al momento, oppure ricreabiliquando necessario, come strutture dati ottenute pre-levando le proprie informazioni da file disponibililocalmente. Nelle due sezioni di codice utilizzate è sta-to aggiunto un metodo NSLog, di stampa a schermo(simile a printf del C) allo scopo di consentire in fa-se di debug di avere un feedback immediato di cosasta succedendo; è comunque consigliato sempre te-stare il proprio applicativo sullo smartphone per ve-rificare se il warning di memoria insufficiente vieneinvocato in qualche situazione. Per forzare questasituazione nel simulatore, che ricordiamo ha acces-so alle nostre risorse hardware, parliamo quindi dialmeno 1GB di RAM, contro i 128MB dell'iPhone 3Ge i 256MB del 3GS, basta, durante una simulazione,cliccare sulla voce “hardware” presente nel menudel simulatore e selezionare la voce “simulate me-mory warning”. Non bisogna ignorare e neppure tra-lasciare una corretta implementazione di questi me-todi, perché possono essere la soluzione per realiz-zare progetti di medie-grandi dimensioni dove le ri-chieste di risorse possono superare quelle disponi-bili.

CONCLUSIONIIn questo articolo, prettamente teorico, abbiamoacquisito ulteriori nozioni, che si riveleranno utiliin qualsiasi contesto vi troverete in futuro; nel pros-simo articolo continueremo a spiegare come vie-ne gestita la memoria e parleremo di retain, re-tainCount, release e altri concetti altrettanto im-portanti . Buona programmazione.

Andrea Leganza

Ottobre 2009/ 53 G

Gestire la memoria nelle applicazioni iPhone � MOBILE

Fig. 3: L'Autorelease distrugge newText traun'invocazione e un'altra di mostraVariabile, generandoun crash

L’AUTORE

Andrea LeganzaLaureato in IngegneriaInformatica, da oltre undecennio realizza soluzionimultimediali e non supiattaforme e con linguaggidiversi. Certificato AdobeACE - Adobe Flex 3 and AIRCertified Expert, e EUCIPCore, appassionato difotografia, linguagiapponese e istruttore dinuoto FIN, è attualmenteimpegnato in numerosiprogetti multimediali,anche con iPhone, conalcune società nazionali edinternazionali; ècontattabile [email protected] odirettamente sul sitowww.leganza.it.

Moltissime classi hanno un prefisso NS,questo è dovuto al fatto che Mac OS X haradici in NeXTSTEP, (di cui NSrappresentano le iniziali) linguaggiorealizzato utilizzando Rhapsody e Mac OS, eche identifica storicamente molte delleclassi che fanno parte delle API utilizzateper lo sviluppo su Mac.

NS*

048-053 def:088-093-corsi-xsl 31-08-2009 18:08 Pagina 53

Page 35: 86746175 iPhone Programming

35 iPhone programming

iPhone programmingI metodi di allocazione e rilascio degli oggetti in memoria

LEAK E ZOMBIE IN AGGUATO...IN QUESTO ARTICOLO APPROFONDIREMO ULTERIORMENTE I CONCETTI LEGATI ALLAGESTIONE DELLA MEMORIA, IN MODO PARTICOLARE FAREMO LA CONOSCENZA DI QUELLICHE IN GERGO VENGONO CHIAMATI ZOMBIE E LEAKS

Prosegue in questo articolo la trattazionedella gestione della memoria. Introdottonel precedente numero della rivista,

l’argomento consentirà una comprensione piùapprofondita dei vari elementi che abbiamoincontrato nel primo articolo, e che applichere-mo, in tutti i futuri articoli dedicati al dispositivomobile sviluppato dalla casa di Cupertino.

RETAIN E RETAINCOUNTCome accennato nel numero precedente, esisto-no due modi per evitare che le proprie variabilivengano rimosse automaticamente dall'Auto -release Pool, utilizzare retain o alloc.Nel primo caso basta invocare tale metododurante la creazione della variabile, dove vieneinvocato come detto in maniera trasparente ilmetodo autorelease, oppure successivamente,all'interno del metodo in cui questa viene creata.

NSMutableString *newText = [[NSMutableString

initWithString: @"Lavoro da fare."] retain];

//inizializzata a retainCount = 1, invocato in maniera

trasparente autorelease (-1 all'istante casuale t=x) e

viene aggiunto 1 utilizzando retain: dopo il passaggio

nell'Autorelease Pool avrà quindi retainCount pari a

1=1(allocazione)-1(autorelease)+1(retain)

oppure:

NSMutableString *newText = [NSMutableString

initWithString: @"Lavoro da fare."];

[newText retain];

In questo modo si è creato un legame indissolu-bile tra la nostra istanza della classe e la variabileche stiamo utilizzando, abbiamo ora il pienocontrollo sul suo tempo di vita, se non rilascere-mo in futuro tale variabile non sarà mai deallo-cata. Associata a retain esiste la variabile internaa ogni oggetto che abbiamo detto chiamarsiretainCount; tale variabile è presente in tutti gli

oggetti che discendono da NSObject (che comeabbiamo detto in un precedente articolo sono lamaggior parte); retainCount assume un valorenumerico intero, è un contatore e indica il nume-ro di oggetti che hanno chiamato il metodoretain su un determinato oggetto, hanno quindidichiarato che la stanno utilizzando, e ne sonoquindi co-proprietari: non venite tentati daldesiderio di utilizzare sistemi di stampa a scher-mo (NSLog) per visualizzarlo, perché otterretespesso valori non coerenti, oppure superiori aquello previsto: per effettuare un'analisi su talevariabile entra in gioco Instruments, di cui parle-remo in un altro articolo. RetainCount assumevalore 1 quando la relativa variabile viene inizia-lizzata. Spieghiamo meglio questo che è uno deiconcetti fondamentali del linguaggio Objective-C: è normale quando si sviluppa un qualunqueapplicativo, passare variabili da una classe a unaaltra invocando i rispettivi metodi, durante que-sta operazione vengono utilizzati come parame-tri le istanze delle classi che abbiamo creato inprecedenza, poiché però, andando a scavare inprofondità parliamo sempre degli amati/odiatipuntatori del C, è stato introdotto il concetto diretain per evitare che tali variabili vengano deal-locate automaticamente se una o più classihanno dichiarato che la stanno utilizzando; inquesto modo si riduce il rischio che avvenganocrash in cascata dovuti alla deallocazione auto-matica effettuata dall'Autorelease Pool (mentrese deallocate manualmente tali variabili tali pro-blemi si possono sempre presentare , e prendonoil nome di Zombie) che, senza tale artificio, nonavrebbe modo di valutare se una variabile è inu-tilizzata oppure no. Ipotizziamo che una classe Ainvochi un metodo passaggioStringa su una clas-se B, passando un parametro di tipo NSString,una semplice stringa di testo (per la precisioneun oggetto testo, differente sintatticamente dallestringhe C che sono array di caratteri) quindi, sequesta seconda classe desiderasse utilizzare inun altro momento, al di fuori del metodo in que-stione, tale variabile senza dover effettuare una

MOBILE � I metodi di allocazione e rilascio degli oggetti in memoria

ht tp ://www. ioprogrammo. i t

G 48 /Novembre 2009

❑ CD ❑ WEBioProgrammoArt4.zip

cdrom.ioprogrammo.it

Conoscenze richiesteOOP

SoftwareMacOS X 10.5.4 osuperiore, XCode

Impegno

¥Tempo di realizzazione

REQUISITI

048-053_versione 7:088-093-corsi-xsl 30-09-2009 12:40 Pagina 48

Page 36: 86746175 iPhone Programming

iPhone programming 36

iPhone programming I metodi di allocazione e rilascio degli oggetti in memoria

ht tp ://www. ioprogrammo. i t Novembre 2009/ 49 G

I metodi di allocazione e rilascio degli oggetti in memoria � MOBILE

copia profonda (deep copy) del suo contenuto inuna variabile locale, potrebbe facilmente effet-tuare la seguente operazione:

//Classe A;

-(void)testRetainCount {

//inivializzazione stringa (è autorelese)

NSMutableString *stringaA = [NSMutableString

initWithString[@”Telefonare assistenza”]];

//retainCount =1, essendo autorelase scenderà

a retainCount=0 in un prossimo futuro, dopo l'uscita

dal metodo corrente e verrà deallocata;

//chiamata del metodo di B passando la stringa

[istanzaB passaggioStringa:stringaA];

//stringaA verrà deallocata in futuro se non

avrà retainCount>0

}

//Classe B

NSMutableString *myString;

-(void)passaggioStringa:(NSMutableString *)stringa{

myString = stringa; //myString punta alla

stessa posizione in memoria di stringa

[stringa retain]; //identico risultato se si

effettua[myString retain]

//stringa ora ha retainCount=2 non verrà deallocata

perchè verrà decrementata a 1 dall'autorelease pool.

}

Poiché stringaA è stata creata immediatamenteprima dell'invocazione del metodo, avràretainCount pari a 1, che diventerà 0 in futuropoiché è autorelease, viene inoltre inserita nel-l'autorelease pool e per tale motivo verrebbe rila-sciata al termine del metodo testRetainCount,ma ciò non avviene poiché all'interno dipassaggioStringa tale valore viene incrementatoa 2, utilizzando retain; la prima riga effettua unasemplice copia tra puntatori, non viene quindicreata alcuna nuova variabile, ma myStringpunta alla stessa locazione di memoria di strin-ga, e non viene incrementato il retainCount distringa, mentre la seconda riga incrementa di 1 ilretainCount della variabile stringa, portandolo alvalore 2; in questo modo si evita che questavenga deallocata automaticamente dall’Autore -lease Pool al termine del metodo testretain Count,infatti fuori da tale metodo avrà valore 1 (dopoche autorelease avrà svolto il suo compito).Effettuare il retain su stringa, oppure su myStringè ininfluente poiché puntano allo stesso oggettoin memoria. Se non utilizzassimo retain all'inter-no di passaggioStringa:

//Classe A;

-(void)testRetainCount {

NSMutableString *stringaA = [NSMutableString

initWithString[@”Telefonare assistenza”]];

//chiamata del metodo di B passando la stringa

[istanzaB passaggioStringa:stringaA];

}

//Classe B

NSString *myString;

-(void)passaggioStringa:( NSMutableString *)stringa{

myString = stringa;

}

-(void)letturaMyString {NSLog (@”%@”,myString)}

All'uscita del metodo passaggioStringa la variabilestringaA verrebbe deallocata dopo l'uscita dalmetodo testReatinCount e ogni accesso successi-vo in A, e in altri metodi di B, a myString(myString e stringaA sono sinonimi come spiega-to) genererebbe un crash del sistema. La lezione da tenere bene a mente è quindi quel-la di cercare di avere una chiara visione deltempo di vita delle variabili per evitare ore didebugging.

RELEASE DI UN OGGETTOQuando decidiamo di notificare all'autoreleasepool che non vogliamo più utilizzare una variabi-le, non vogliamo quindi esserne proprietari(come è stato detto è più opportuno dire copro-prietari con altre istanze), invochiamo su di essail metodo release, che decrementa di un'unità ilsuo retainCount: utilizziamo il comando [nome-variabile release], in questo modo, se la nostravariabile aveva retain count pari a 1 scenderà a 0e verrà rilasciata immediatamente: attenzione,quindi, se viene deallocata non potrete più acce-dervi, e, nel caso lo faceste, causerete un crashdel sistema (questa operazione errata di accessoviene identificata con l'accesso a uno Zombie, ene parleremo verso la fine di questo articolo).Le regola per evitare di incorrere in decine di pro-blemi di gestione della memoria è quella dibilanciare il numero di retain con quello di relea-se, in tal modo, quando si effettuerà l'ultimorelease, si farà sempre scendere il retainCount a 0e l'Autorelease pool invocherà il metodo dealloc()sulla variabile (il metodo che svolge la funzionedi distruttore, quindi responsabile della rimozio-ne delle risorse utilizzat: variabili, strutture dati ecanali di comuncazione in primis); ovviamente,se si desidera mantenere in vita un'istanza pertutto il tempo di vita della propria applicazionetale numero dovrà essere sempre maggiore di 0.Un esempio pratico della variazione dellavariabile retainCount è quello di pensare alnumero di scalini necessari per raggiungere unpiano, il numero di passi da effettuare per sali-re sulla sommità prima e poi riscendere è sem-pre uguale.

048-053_versione 7:088-093-corsi-xsl 30-09-2009 12:40 Pagina 49

Page 37: 86746175 iPhone Programming

37 iPhone programming

iPhone programmingI metodi di allocazione e rilascio degli oggetti in memoriaMOBILE � I metodi di allocazione e rilascio degli oggetti in memoria

ht tp ://www. ioprogrammo. i tG 50 /Novembre 2009

Invece di release, che effettua il decrementoimmediatamente, si può anche invocaremanualmente autorelease, che provvederà adecrementare retainCount in futuro: questoapproccio non è consigliabile ed è meglio rila-

sciare subito una variabile, sia per liberare ilprima possibile una risorsa (e questa è una rego-la che si dovrebbe applicare in ogni linguaggio diprogrammazione, ma nel contesto mobile è difondamentale importanza dove le risorse sonopiù limitate), ma anche per non incorrere neiproblemi di accesso precedentemente spiegati,in primis a variabili deallocate automaticamentein un momento non precisato. È possibile invocare numerose volte release? Insequenza certamente, ma non è consigliabile selo si effettua per risolvere problemi di gestionenon corretta della memoria, ad esempio quandosi dealloca un oggetto e non si effettua il releasenel suo distruttore sulle variabili che questa uti-lizzava e si bypassa il problema in questo modo:generalmente rispecchia una cattiva compren-sione del concetto di retain/release, può comun-que risultare necessario in particolari situazioni.Molti metodi effettuano un retain automatico, adesempio quando aggiungiamo una variabile a unarray, e dal punto di vista pratico risultaun'operazione automatica molto utile, poiché glioggetti inseriti vengono segnalati automatica-mente di proprietà di un determinato oggetto e

non verranno rilasciati se non quando l'arrayverrà rilasciato; questa operazione automaticapuò risultare però causa di problemi quali leaks ezombie (spiegati in questo stesso articolo). Se sivuole deallocare immediatamente una variabile,disinteressandosi del retainCount, basterà invo-care il metodo dealloc() su di essa, questa opera-zione e però sconsigliata, poiché, ignorando ilretainCount, si ignora quante altre istanze lastanno al momento utilizzando e si potrebberoavere numerosi crash del sistema (Zombie).Nella documentazione dell'API viene dichiaratose un certo metodo di una classe effettua oppureno il retain / release sull'oggetto passato comeparametro, è comunque possibile utilizzareInstruments per verificare tale comportamento.

COSA SONO I LEAKNon effettuare correttamente la gestione dellerisorse in memoria porta ai cosiddetti leaks.Con tale termine si identifica quella variabile chenon è più accessibile all'interno di alcun metododi una classe e non vi è quindi alcun modo perinvocare su di essa il release/autorelease/dealloc:la variabile è quindi irraggiungibile dal codicerealizzato dall'utente e, essendo gestita dall'u-tente, non può essere rimossa automaticamentedall'Autorelease Pool; in questo modo avremo undispendio di memoria, in genere incrementale,che potrebbe portare, quando si parla di unnumero di variabili medio grande (a secondadella loro singola occupazione di memoria), allachiusura forzata del nostro programma. Un leakè quasi sempre, quando non è un bug di cui sonoresponsabili i tecnici di casa Apple (avviene adesempio quando si utilizza il parser NSXML),segnale di una programmazione errata, disatten-ta o poco consapevole. Per monitorare tali situa-zioni si utilizza il tool chiamato Instruments, chefornisce in real-time dettagli sul numero e su

Fig. 1: Un tipico ciclo di vita di una variabile, quando il retaincount raggiunge il valore 0 viene automatica-mente rilasciata; si noti inoltre il bilanciamento dei retain con quello dei release, che permettono di farlo scendere a 0

ULTERIORIAPPROFONDIMENTIPer approfondire i concettimostrati in questo articolo,

consultare la guidachiamata Memory

Management ProgrammingGuide for Cocoa disponibile

su sitodeveloper.apple.com o

all'interno delladocumentazione fornita con

Xcode

NOTA

Fig. 2: Durante il tempo di vita di una variabile il retainCount sale e scende, fino generalmente a terminare in un determinato istante t

048-053_versione 7:088-093-corsi-xsl 30-09-2009 12:40 Pagina 50

Page 38: 86746175 iPhone Programming

iPhone programming 38

iPhone programming I metodi di allocazione e rilascio degli oggetti in memoria

ht tp ://www. ioprogrammo. i t Novembre 2009 / 51 G

I metodi di allocazione e rilascio degli oggetti in memoria � MOBILE

quale sequenza di chiamate/comandi hannogenerato tale leak. Un esempio di leak è statodescritto nel paragrafo precedente, quandoabbiamo parlato del mancato rilascio delle varia-bili inserite in un array; in tal caso, se non avre-mo più accesso diretto all'array, non riusciremopiù a liberare gli oggetti inseriti al suo interno,che diverranno quindi leak.

IL METODO ALLOCIl secondo metodo per evitare che una variabilevenga deallocata automaticamente, è quello dicrearla utilizzando il metodo alloc, che imposta ilsuo retainCount a 1 (effettua quindi un retainautomatico), e sarà quindi nostra cura decre-mentarlo utilizzando release/autorelease perconsentire, poi, nel caso tale valore raggiungavalore 0, all'Autorelease Pool di invocare su diesso il metodo dealloc. Quando si usa alloc, sirichiede che venga allocato lo spazio in memorianecessario per contenere tale istanza. Per inizia-lizzare il suo contenuto si invoca generalmenteun metodo che inizia con il prefisso init, checompleta, quindi, la fase di impostazione del suostato interno. La mancata invocazione di unmetodo init su un'istanza, la rende inutilizzabilenella pratica; poiché nel linguaggio Objective-Cnon esiste una regola per identificare un costrut-tore, e non è neppure obbligatorio crearlo, anchese risulterebbe di scarsa utilità non realizzarnealmeno uno, si ha la possibilità di creare unnumero indefinito di costruttori che soddisfinole proprie necessità. Generalmente, comunque,viene utilizzato il prefisso init per ognuno di que-sti, quindi basterà digitare init quando compa-rirà la lista di autocomplete per verificare qualisono stati resi disponibili. Il costruttore più sem-plice è caratterizzato dalla seguente signature -(id) init(): di questo ne parleremo approfondita-mente in un prossimo articolo.

NSMutableString *stringa;

stringa = [[NSMutableString alloc] initWithString:

@”testo”]; //retainCount=1

In questo caso abbiamo creato un'istanza di unoggetto appartenente alla classe NSMutable -String, in grado di contenere una stringa didimensione variabile, prima definendola, poiallocandola e infine inizializzando il suo valoreinterno con una stringa; il retainCount è pari a 1e potremo utilizzarla in qualunque metodo noidesideriamo, poiché il contatore non verrà maidecrementato automaticamente. Quando sarànecessario, potremo invocare su di essa il meto-do release e farla deallocare, è quindi sempre vali-

da la regola del rapporto uno a uno tra retain erelease. Attenzione, se la variabile viene dichia-rata e definita in un metodo, è quindi locale allostesso, così come nel caso che segue:

- (void) initMyString {

NSMutableString *stringa;

stringa= [[ NSMutableString alloc]

initWithString:@"testo"];

[stringa release];

}

se non si provvede a rilasciarla al suo interno,prima della fine del metodo, avremo un leak, poi-ché fuori da questo non sarà più possibile acce-dervi e rilasciarne la memoria.Un utilizzo che non presenta tale inconvenienteè quello in cui la variabile appartiene a una istan-za della classe, ed è accessibile in qualunque suometodo interno:

NSMutableString *stringa;

- (void)initMyString {

stringa = [[ NSMutableString alloc] initWithString:@"testo"];

}

-(void) removeString {[stringa release];

}

Poiché abbiamo accennato dell'inserimento dioggetti in un array, viste le conoscenze ora acqui-site, si può affrontare il problema in due modi:utilizzando come struttura dati un NSMutableArray, ovveroun array la cui dimensione puòcambiare dinamicamente, semplicementeaggiungendo/rimuovendo oggetti e, quindi,invocando alcuni metodi. Leggiamo prima unestratto della documentazione associata a taleclasse:

If you do not use garbage collection, when you add anobject to an array, the object receives a retain message.When an object is removed from a mutable array, itreceives a release message. If there are no further ref-erences to the object, this means that the object isdeallocated.

Poiché, come è stato già detto in precedenza,non siamo in in ambiente con un vero GarbageCollector, ogni oggetto che verrà inserito nell'ar-ray riceverà un retain, mentre riceverà un releasequando verrà rimosso, oppure quando si invo-cherà release sull'array stesso. Simuliamol'inserimento:

NSMutableArray *array = [[NSMutableArray alloc] init];

for (int i = 0; i < 10; i++) {

NSMutableString *convenienceString=

[NSMutableString stringWithString:@"testo"];

048-053_versione 7:088-093-corsi-xsl 1-10-2009 15:55 Pagina 51

Page 39: 86746175 iPhone Programming

39 iPhone programming

iPhone programmingI metodi di allocazione e rilascio degli oggetti in memoriaMOBILE � I metodi di allocazione e rilascio degli oggetti in memoria

ht tp ://www. ioprogrammo. i t

G 52 /Novembre 2009

//retainCount 1 e autorelease

[array addObject:convenienceString];

//retain automatico del metodo sul parametro

retainCount 2 di convenienceString

}

...autorelease automatico al tempo x: su tutte le

variabili inserite nell'array assumono retainCount=

1...

[array release] //array invia release alle proprie

variabili quindi il loro retainCount scende a 0 e

vengono deallocate, array viene ovviamente rilasciato

dopo questa operazione e deallocato se il suo

retainCount=0 (non effettua un controllo se il

retainCount degli oggetti al suo interno è >0)

Non abbiamo utilizzato alloc, quindi tutte leistanze di convenienceString vengono rilasciateautomaticamente quando rilasceremo l'array(perchè sono autoreleased). convenienceStringassumerà retainCount pari a 2 poiché addObject,da come è dichiarato esplicitamente dalla docu-mentazione dell'API, effettua un retain sul para-metro passato al metodo addObject; essendo ditipo autorelease ognuna delle dieci istanze inse-rite nel ciclo for verrà decrementata a 1 automa-ticamente, il successivo rilascio dell'array effet-tuerà un'ulteriore release su ognuno di questi, eil loro retainCount verrà portato a 0, sarà perciòinvocato il dealloc su ogni variabile di tipoNSMutableString che abbiamo inserito.

NSMutableArray *array = [[NSMutableArray alloc] init];

for (int i = 0; i < 10; i++) {

NSMutableString *convenienceString=

[NSMutableString stringWithString:@"testo"];

[array addObject:convenienceString];

//retaincount di convenienceString = 2

[convenienceString release]; // retaincount di

convenienceString = 1

[array release]; //array invia release agli oggetti

che contiene, quindi convenienceString assumerà

retainCount=0 e verrà dellocato; non si avranno quindi leaks

}

In questo caso abbiamo utilizzato alloc, quindiè necessario rilasciare la variabile per evitareleaks, poiché altrimenti tutte le istanze diallocedNumber presenti nell'array avrannoretainCount pari a 2 e, quando proveremo a rila-sciare l'array, il loro retainCount scenderà a 1impedendone il rilascio e generando un leakdieci in questo preciso esempio, (poiché deallo-chiamo l'array non potremo più accedervi e,conseguentemente, non avremo modo di acce-dere a queste variabili con retainCount pari a 1).Questo secondo approccio ha il pregio di libera-

re immediatamente le risorse, e risulta quindimolto adatto in situazioni in cui si genera unnumero consistente di inserimenti.Confrontiamo le due soluzioni: nel primo casoavremo un numero di oggetti incrementale, sipasserà da 1+1 della prima iterazione a n+nquando si arriverà all'ultima, tale numerodiventerà poi pari a n solo dopo il termine delmetodo, quando le istanze allocate riceverannol'autorelease; nel secondo caso a ogni iterazioneavremo in memoria un numero di oggetti pari aquelli inseriti nell'array e a quello che verrà rila-sciato alla fine dell'iterazione, quindi da 1+1 an+1 (n istanze inserite nell'array e l'oggetto allo-cato di tipo NSNumber). Esiste una terza alter-nativa, che consiste nell'utilizzare un Pool loca-le a tale metodo che conterrebbe le variabilicreate nel primo esempio e le rilascerebbe altermine del ciclo, ma non complichiamo ulte-riormente. Bisogna comunque tenere quindi amente certe considerazioni quando si lavora sustrutture dati popolate con oggetti di dimensio-ni non ridotte.

ZOMBIE E EXC_BAD_ACCESSEXC_BAD_ACCESS rappresenta uno di queglierrori che riceverete più frequentemente quandoinizierete a creare istanze di classi in Objective-C,questo errore viene generato quando il vostrocodice tenterà di accedere ai cosiddetti Zombie,sinonimo di istanze di classi non più disponibili,perché deallocate in precedenza. Prendiamo inconsiderazione come esempio il seguente codice:

NSMutableString *stringa;

-(void) scatenaZombie {

[self initMyString]; [self printMyString];

}

- (void) initMyString {

stringa = [[ NSMutableString alloc]

initWithString:@"testo"];

[stringa release]; //la variabile viene deallocata

}

-(void) printMyString {

NSLog (“%@”,stringa); //accesso ad uno zombie

}

Inizializzando nel metodo initMyString la nostrastringa, e successivamente deallocandola richia-mando il metodo release, abbiamo posto fine alsuo tempo di vita; se però invocheremo successi-vamente a tale metodo quello chiamatoprintMyString riceveremo un errore da parte deldebugger in esecuzione, poiché tale metodoprova ad accedere a un'istanza non più disponi-

NOTA

SNOW LEOPARD E XCODE 3.2

I possessori di SnowLeopard hanno modo diutilizzare il software di

analisi statica chiamatoClang, integrato con XCode

3.2. Purtroppo non èinstallabile su Leopard

10.5. Questo tool fornisceinformazioni dettagliate sututti i punti in cui possono

verificarsi Zombie e Leaks:basterà invocare il

comando ‘Build andAnalyze’ presente nel

menu Build.

048-053_versione 7:088-093-corsi-xsl 30-09-2009 12:40 Pagina 52

Page 40: 86746175 iPhone Programming

iPhone programming 40

iPhone programming I metodi di allocazione e rilascio degli oggetti in memoria

ht tp ://www. ioprogrammo. i t

bile. Questo errore si presenta anche nel caso incui si sbilanci il numero di retain e release, invo-cando quindi un numero di questi ultimi mag-giore del retainCount disponibile:

-(void) scatenaZombie {

[self initMyString];

[self removeMyString];

}

- (void) initMyString {

stringa = [[ NSMutableString alloc]

initWithString:@"testo"];

[stringa release]; //la variabile viene deallocata

}

-(void) removeMyString {

[stringa release]; //secondo release : accesso ad

uno zombie

}

Purtroppo questo tipo di errore non viene corre-dato da informazioni sufficienti per comprende-re in quale porzione di codice avviene il proble-ma, e per risolvere tale mancanza del debugger ènecessario impostare un particolare parametrodi configurazione all'interno delle opzioni del fileeseguibile generato da XCode. Fate doppio clicsulla voce (ha il nome del vostro progetto) pre-sente come riga all'interno nella colonna di sini-stra di XCode, Goups & Files, sotto la categoriaExecutables e selezionate Arguments dal menu inalto; inserite una riga all'interno del boxVariables to be set in the Enviroment con il seguen-te valore: NSZombie Enabled e impostando comevalore YES. Dopo aver effettuato questa opera-zione, invece di un laconico EXC_BAD_ACCESS,otterremo una stringa leggermente più interes-sante.

*** -[CFString retain]: message sent to deallocated

instance 0xd21b50

Questa riga di errore ci fornisce l'indirizzo inmemoria dell'oggetto che ha causato il crash delsoftware, ma non è ancora sufficientementecomodo per identificare velocemente qualeistanza genera il problema. Questa impostazionedeve essere utilizzata solamente in fase di svilup-po e non in fase di rilascio poiché non rilasciaalcuna variabile allo scopo di consentire lo stu-dio delle variabili Zombie! Una soluzione per evi-tare di dimenticarsi tale impostazione attiva,consiste nell'inserire il seguente codice nel fileNomeApplicazioneDelegate.m come prima rigadel metodo applicationDidFinishLaunching:

- (void)applicationDidFinishLaunching:(UIApplication

*)application {

if(getenv("NSZombieEnabled")) {

NSLog(@"NSZombieEnabled!!! Disable when

compiling for release");}...

Ricordatevi sempre di disabilitarla quando com-pilerete il vostro software per l'Apple Store. Per avere ulteriori informazioni su tale errorebisogna impostare un breakpoint che permetteràdi avviare il debugger con un dettagliato, e piùutile, stack delle chiamate. Selezionando la vocedel menu Run->Show->BreakPoints si presenteràuna finestra dove è possibile inserire e visualizza-re i breakpoint impostati. Aggiungiamone unoinserendo il seguente testo:

-[_NSZombie methodSignatureForSelector:]

Impostando un breakpoint su tale metodo, sifarà in modo che il debugger interrompal'esecuzione dell'applicativo prima che questocrashi, fornendo informazioni sullo stack trace emostrando quale variabile ha generato il crash.Quando si verificherà un accesso a uno Zombiepotrete quindi ottenere informazioni dettagliate,ma soprattutto il nome della variabile e identifi-care il metodo in cui è avvenuto l'errore. Questosistema non funziona purtroppo con tutte leclassi fornite dall’SDK, e in tal caso diventa tuttopiù complicato. Per ridurre al minimo questoproblema di accesso, basta impostare a nil ilvalore della variabile, immediatamente dopo ilsuo rilascio (dopo il release o autorelease), in talmodo qualunque invocazione non avrà alcunrisultato, ma almeno non farà crashare il softwa-re.

CONCLUSIONITutto quello che abbiamo spiegato in questi duearticoli si applica a quasi tutte le classi discen-denti da NSObject (NSString ad esempio si com-porta in modo diverso, e spiegheremo il perchènel prossimo articolo), purtroppo perde di vali-dità quando utilizziamo strutture dati e tipi didati presi direttamente dal linguaggio C, adesempio creando variabili di tipo int o float, chein mano a un programmatore accorto possonofare la differenza in termini di prestazioni per unsoftware. In questo articolo, prettamente teorico,abbiamo acquisito ulteriori nozioni, che si rivele-ranno utili in qualsiasi contesto vi troverete infuturo; nel prossimo numero della rivista torne-remo a trattare della popolazione della tabella emodificheremo il codice creato in automaticoper realizzarla; mostreremo anche come popola-re le relative celle. Buona programmazione.

Andrea Leganza

Novembre 2009/ 53 G

I metodi di allocazione e rilascio degli oggetti in memoria � MOBILE

NOTA

ZOMBIE E LEAKSSi accede a uno zombiequando si invocano metodio si richiedono variabili diun'istanza non piùdisponibile, perchédeallocata (autoreleaseautomatico oppure releasemanuale); un leak, invece,identifica un'istanza nonpiù accessibile da parte delcodice, poiché non è statadeallocata quando erainvece necessario.

L’AUTORE

Andrea LeganzaLaureato in IngegneriaInformatica, da oltre undecennio realizza soluzionimultimediali e non supiattaforme e con linguaggidiversi. Certificato AdobeACE - Adobe Flex 3 and AIRCertified Expert, e EUCIPCore, appassionato difotografia, linguagiapponese e istruttore dinuoto FIN, è attualmenteimpegnato in numerosiprogetti multimediali,anche con iPhone, conalcune società nazionali edinternazionali; ècontattabile [email protected] odirettamente sul sitowww.leganza.it.

048-053_versione 7:088-093-corsi-xsl 1-10-2009 17:07 Pagina 53

Page 41: 86746175 iPhone Programming

41 iPhone programming

iPhone programmingLa gestione delle tabelle negli applicativi Apple iPhone

COME POPOLARE UNA UITABLEVIEWIN QUESTO ARTICOLO POPOLIAMO LA NOSTRA TABELLA CON UN ELENCO DI VOCIOTTENUTE INTERROGANDO UNA SERIE DI STRUTTURE DATI. NELLA FATTISPECIE VEDREMOCOME INSERIRE DELLE SEMPLICI RIGHE, CREARE DELLE SEZIONI E NAVIGARE TRA LE STESSE

Nei numeri precedenti di questa secondaserie di articoli dedicati alla programma-zione dell’iPhone abbiamo momenta-

neamente abbandonato il nostro progetto, unaToDo List, per trattare alcuni argomenti di fonda-mentale importanza relativi al linguaggio Objec-tive-C e alla gestione della memoria in unambiente non governato da un moderno stru-mento automatico qual è il Garbage Collector.Riprendiamo ora a trattare della popolazionedella UITableView che avevamo creato utilizzan-do il wizard di XCode e selezionando il progettodi tipo Navigation-based Application.

METODI PER POPOLAREUNA TABELLAPer semplicità riproponiamo il corpo del metodotabelView;cellForRowAtIndexPath che viene genera-to automaticamente dal wizard:

// Customize the appearance of table view cells.

- (UITableViewCell *)tableView:(UITableView

*)tableView cellForRowAtIndexPath:(NSIndexPath

*)indexPath {static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView

dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {

cell = [[[UITableViewCell alloc]

initWithStyle:UITableViewCellStyleDefault

reuseIdentifier:CellIdentifier] autorelease];

}

// Configure the cell.

return cell;

}

Poiché abbiamo sufficientemente trattato in prece-denza di alloc, autorelease e nil, iniziamo immedia-tamente a popolare la nostra tabella nel modo piùsemplice: inserendo “manualmente” una stringa ditesto all’interno di tutte le righe della nostra lista dicose da fare. Prima di ciò, è doveroso descrivere

molto brevemente la struttura tipica di una cella,almeno riguardo le versioni fornite nell’SDK. Una cella svolge la funzione di unità atomica di ognitabella, ma in pratica è un contenitore anch’essa dinumerosi componenti: trovano posto, almeno nelleversioni predefinite, due istanze di UILabel, unadisponibile con l’identificatore di textLabel, l’altracome detailTextLabel, una UIView con il nomeaccessoryType, un UIImageView con il nome diimageView, e terminiamo con una UIView chiamatabackgroundView; in realtà la lista di componentidisponibili non è terminata, ma generalmente que-sti sono quelli utilizzati nella maggior parte di appli-cazioni, a meno di realizzare customizzazioni relati-vamente complesse; le due UILabel svolgono ilcompito di mostrare il testo associato alla riga,l’accessoryType consente di impostare il tipo di iconada posizionare sulla destra della cella per indicare lapossibile presenza di un ulteriore livello di dettaglio,la imageView, quando viene associata aun’immagine, la mostrerà sul lato sinistro dellacella, prima del testo, la backgroundView consentedi variare in maniera relativamente avanzatal’aspetto della cella.Nel caso in cui variare le impostazioni di questicomponenti non ottenesse i risultati sperati, sipotranno aggiungere uno o più oggetti accedendoalla proprietà chiamata contentView, anch’essa unUIView, che svolgerà per questi nuovi oggetti la fun-zione di superview: in caso di necessità sarà quindiquesto componente che dovrete utilizzare per inse-rire altre UILabel, immagini o altro; se invece sirichiedesse una completa ristrutturazione della sin-gola cella, sarà necessario ignorare l’inizializzazioneutilizzando gli stili predefiniti (quindi il metodoinitWithStyle:reuseIdentifier utilizzato dal wizard) einvocare la versione minimale dell’inizializzazione(init) e inserire e configurare manualmente ognicomponente (impostando posizioni, dimensioni eparametri). Per effettuare una customizzazione èanche possibile utilizzare Interface Builder, ma nontratteremo di questo aspetto nel presente articolo. Per rendersi conto delle potenzialità delle customiz-

MOBILE � La gestione delle tabelle negli applicativi Apple iPhone

ht tp ://www. ioprogrammo. i t

G 44 /Dicembre 2009

❑ CD ❑ WEBioProgrammoArt4.zip

cdrom.ioprogrammo.it

Conoscenze richiesteOOP

SoftwareMacOS X 10.5.4 osuperiore, XCode

Impegno

¥Tempo di realizzazione

REQUISITI

044-049:088-093-corsi-xsl 29-10-2009 17:33 Pagina 44

Page 42: 86746175 iPhone Programming

iPhone programming 42

iPhone programming La gestione delle tabelle negli applicativi Apple iPhone

ht tp ://www. ioprogrammo. i t Dicembre 2009/ 45 G

� MOBILE

zazioni possibili per le celle basta semplicementeaprire i settings/impostazioni disponibili su iPhone.Quasi ogni schermata presenta una diversa versionedi cella. Generalmente si impostano le caratteristi-che estetiche comuni a tutte le celle, e che per talemotivo resteranno immutate per tutta la vita dellatabella, all’interno del blocco di testo in cui questecelle vengono allocate e inizializzate (quindi all’in-terno del blocco relativo a if(cell==nil)); immediata-mente dopo di questo si inserirà il codice necessarioa customizzare la riga, in base al contenuto relativoalla singola variabile associata.Modifichiamo il tipo di cella passando dal tipo pre-definito UITableViewCellStyleDefault a quello UITableViewCellStyleValue1 per consentirci di inserireun testo nella parte destra della cella.

// Customize the appearance of table view cells.

...

if (cell == nil) {

cell = [[[UITableViewCell alloc]

initWithStyle:UITableViewCellStyleValue1

reuseIdentifier:CellIdentifier] autorelease];

...

}

// Configure the cell.

//Oltre questo commento inseriremo il codice per

modificare i testi delle celle

Ora possiamo impostare il testo che verrà visualiz-zato per tutte le righe; per fare ciò dovremo accede-re a uno dei componenti visuali contenuti nellacella, identificato dal nome textLabel, tale compo-nente, come è stato detto, non è altro che unaUILabel che consente di mostrare a schermo unastringa di testo semplicemente impostando il valo-re della sua proprietà text:

...

cell = [[[UITableViewCell alloc]

initWithStyle:UITableViewCellStyleValue1

reuseIdentifier:CellIdentifier] autorelease];

// Configure the cell.

cell.textLabel.text= @"Riga di testo";

return cell

}

Avviamo il simulatore, notiamo però che non verrà vi-sualizzato alcun testo: questo è dovuto al fatto che,come abbiamo spiegato in precedenza, ogni tabellarichiede al proprio delegate (in questo caso la nostraclasse RootViewController generata dal wizard), at-traverso l’invocazione su di questo di due metodi num-berOfSectionsInTableView: e tableView: numberO-fRowsInSection, il numero di sezioni e quello di righeper ogni sezione disponibile:

- (NSInteger)numberOfSectionsInTableView:

(UITableView *)tableView { return 1;}

// Customize the number of rows in the table view.

- (NSInteger)tableView:(UITableView *)tableView

numberOfRowsInSection:(NSInteger)section {

return 0; }

Come si può notare, per default il wizard restituisceper ogni sezione presente, una in questo caso, ilvalore zero (0): ciò impedisce la creazione di una opiù celle: modifichiamo con un valore maggiore dizero, ad esempio 15:

// Customize the number of rows in the table view.

- (NSInteger)tableView:(UITableView *)tableView

numberOfRowsInSection:(NSInteger)section

{ return 15; }

Salviamo e avviamo il simulatore: finalmente otte-

niamo un elenco, di scarsa utilità pratica, ma diindubbio valore didattico. Proviamo ora ad aggiun-gere un testo nella parte destra. Questa operazionela si compie, così come abbiamo fatto per ilcell.textLabel, accedendo a cell.detailTextLabel, cheè sempre un’istanza della classe UILabel:

...

cell = [[[UITableViewCell alloc]

initWithStyle:UITableViewCellStyleValue1

reuseIdentifier:CellIdentifier] autorelease];

cell.textLabel.text= @"Riga di testo";

cell.detailTextLabel.text = @"Testo";

Se avessimo utilizzato come style in fase di inizializzazionedella cella quello di default (initWithStyle:UITable

Fig. 1: La tabella mostra un numero di righe a secondadel valore ottenuto invocando tableView:numberOfRowsInSection:

La gestione delle tabelle negli applicativi Apple iPhone

NOTA

RIFERIMENTI WEBCreazione dell'account per scaricare l'SDKe consultare ladocumentazione:http://developer.apple.com/iphone

044-049:088-093-corsi-xsl 29-10-2009 17:33 Pagina 45

Page 43: 86746175 iPhone Programming

43 iPhone programming

iPhone programmingLa gestione delle tabelle negli applicativi Apple iPhoneMOBILE � La gestione delle tabelle negli applicativi Apple iPhone

ht tp ://www. ioprogrammo. i tG 46 /Dicembre 2009

ViewCellStyleDefault), avremo ottenuto come risul-tato la visualizzazione di tale stringa di testo imme-diatamente sotto textLabel, con questo stile, invece,tale UILabel è presentata nella modalità che riteniamopiù comoda per una rapida consultazione di una to-do list. Impostiamo infine il simbolo grafico di detta-glio, disclosure button (Fig. 2), identificato con il sim-bolo di maggiore, per fare ciò dovremo impostare ta-le proprietà delle celle all’interno del metodo in cuicreiamo le istanze delle celle, parliamo quindi sem-pre di tabelView;cellForRowAtIndexPath:

/if (cell == nil) {

cell = [[[UITableViewCell alloc]

initWithStyle:UITableViewCellStyleValue1

reuseIdentifier:CellIdentifier] autorelease];

cell.accessoryType =

UITableViewCellAccessoryDisclosureIndicator;

}

Completiamo questa prima versione impostando iltitolo che viene mostrato nella Navigation Bar, labarra di colore blue posizionata nella parte più altadella nostra applicazione, per fare ciò decommen-tiamo il metodo viewDidLoad e aggiungiamo laseguente riga:

- (void)viewDidLoad {

[super viewDidLoad];

self.title = @"ToDo List";

// Uncomment the following line to display an Edit

button in the navigation bar for this view controller.

// self.navigationItem.rightBarButtonItem =

self.editButtonItem;

}

Perchè abbiamo inserito questo semplice comandoall’interno del metodo viewDidLoad? Questo è unodei primi metodi che vengono invocati esplicita-mente (ancora prima viene invocato il metodoinitWithNibName) quando viene analizzato il filexib e l’engine del telefono si prepara per mostrarlovisivamente: dopo viewDidLoad ogni istanza, diret-tamente o indirettamente legata a UIViewController,invocherà in sequenza viewWillAppear e viewDidAppear (anche questi inseriti automaticamente dalwizard e anche questi da decommentare in caso dinecessità); la scelta è caduta su viewDidLoad perchéè uno dei metodi consigliati dalla stessa documenta-zione per effettuare al suo interno operazioni neces-sarie per la configurazione sia visuale che struttura-le del viewcontroller. Cambiamo ora il tipo di tabel-la da plain, quindi visualizzata come un continuoelenco in cui il nome delle sezioni viene presentatocome un testo su uno sfondo blu scuro, a grouped,allo scopo di distanziare meglio i diversi giorni dellasettimana (senza doverci preoccupare di impostareparametri per gli header e i footer), per fare ciòapriamo il file rootviewcontroller.xib con InterfaceBuilder e, dopo aver selezionato la tabella, utilizzan-do il Property Inspector, selezioniamo grouped dalla

voce a tendina. A questo punto testiamo il compor-tamento della tabella aumentandone il numero disezioni, impostiamo a 7, i giorni della settimana chemostreremo nella schermata:

- (NSInteger)numberOfSectionsInTableView:

(UITableView *)tableView { return 7;}

Mostriamo ora il titolo per ognuna delle sette sezio-ni, il giorno della settimana nel nostro caso; per fareciò utilizzeremo il metodo richiesto dalla tabella allanostra classe/delegate, la cui signature è tableView:titleForHeaderInSection:; tale metodo riceve comeparametro un valore numerico a indicare la sezioneche la tabella sta analizzando, un numero nell’inter-vallo compreso tra da 0 e numerosezioni-1 (7-1 = 6in questo caso);

- (NSString *)tableView:(UITableView *)tableView

titleForHeaderInSection:(NSInteger)section

{

switch (section) {

GLI STILIPREDEFINITI

Gli stili predefiniti per le celle,introdotti nell’SDK 3.0, sono :UITableViewCellStyleDefault,UITableViewCellStyleValue1,UITableViewCellStyleValue2,

UITableViewCellStyleSubtitle;

NOTA

Fig. 2: Abbiamo ora ottenuto una struttura base per una tabella

Fig. 3: Impostiamo a grouped il tipo di visualizzazionedella tabella

044-049:088-093-corsi-xsl 29-10-2009 17:33 Pagina 46

Page 44: 86746175 iPhone Programming

iPhone programming 44

iPhone programming La gestione delle tabelle negli applicativi Apple iPhone

ht tp ://www. ioprogrammo. i t Dicembre 2009 / 47 G

La gestione delle tabelle negli applicativi Apple iPhone � MOBILE

case 0: return @"Sunday"; break;

case 1: return @"Monday"; break;

case 2: return @"Tuesday"; break;

case 3: return @"Wednesday"; break;

case 4: return @"Thursday"; break;

case 5: return @"Friday"; break;

case 6: return @"Saturday"; break;

default: return @""; break; }

return @"";

}

Per ora abbiamo sufficientemente strutturato lanostra interfaccia grafica, dobbiamo ora realizzareun sistema per gestire le nostre informazioni inmodo strutturato e facilmente modificabile.

NSARRAY E NSMUTABLEARRAYQuando si mostra il contenuto di una tabella è neces-sario accedere in qualche modo al componente Mo-del del pattern MVC, mentre la tabella incarna il View(e il Controller): a seconda delle necessità,si deve sce-gliere tra una struttura dati di tipo statico, immutabi-le dopo la sua creazione o dinamico (nella documen-tazione queste classi contengono generalmente il ter-mine mutable). Tra le strutture disponibili vengonospesso adoperate NSArray e NSMutableArray, facen-ti parte della libreria Foundation.framework; la primaè una classe il cui scopo è quello di contenere un elen-co di istanze di altre classi, impedendone però qua-lunque modifica successivamente alla sua creazionein termine di numero e istanza: non è quindi possibi-le cancellare oppure inserire nuovi elementi; NSMu-tableArray è semplicemente una versione opportu-namente modificata di NSArray (è infatti una sua clas-se direttamente derivata) che consente inserimenti,in praticamente ogni posizione, testa, coda e nel mez-zo, e cancellazioni in maniera completamente libera.La scelta di quale struttura sia la più adatta in un de-terminato contesto è un’operazione generalmenteimmediata: se il numero di celle può cambiare neltempo, prevalentemente ad opera dell’interazionedell’utente, la scelta in genere cade su NSMutableAr-ray per evitare inutili operazioni di cancellazione e ri-popolamento della struttura dati: inserire un nuovooggetto in un NSMutableArray è una singola opera-zione, mentre nel caso di un NSArray comporta una co-pia profonda della struttura precedente, con un nu-mero di iterazioni quasi sempre pari al numero di ele-menti presenti (effettuati con un ciclo for oppure uti-lizzando la fast enumeration ad esempio). La rimo-zione di un elemento da un NSMutableArray è anchein questo caso ottenuta con una singola operazione,mentre nel caso dell’NSArray ciò comporta sempreeffettuare una copia profonda nella quale viene igno-rato l’elemento da cancellare. La grande flessibilità di

NSMutableArray le impedisce però di essere la soluzionepiù performante rispetto ad NSArray, sia per i mec-canismi interni necessari per farla operare, che in ter-mini di occupazione di memoria a causa del codiceaggiuntivo inserito al suo interno sotto forma di me-todi e altre strutture aggiuntive. Per fortuna converti-re una struttura di un tipo all’altro nel caso si sia rive-lata la scelta meno adatta è un’operazione relativa-mente indolore (più leggera da NSArray a NSMuta-bleArray, viceversa più complessa). Nel nostro caso,per ridurre il numero di righe di codice da digitare,utilizzeremo NSMutableArray, per gestire i vari taskdelle singole giornate, mentre un NSArray per rag-gruppare le informazioni relative ai sette giorni dellasettimana.

NSDICTIONARY ENSMUTABLEDICTIONARYOra che abbiamo definito quale struttura dati con-terrà i nostri elenchi di cose da fare, si pone il proble-ma di come rappresentare il singolo task della nostratodo list: analizzando la sua struttura identifichiamoalcune informazioni che identificherebbero il suo sta-to: una descrizione, che mostreremo nella textLabele lo stato (Da fare, Fatto, Dimenticato) che verrà utilizzatoper le UILabel di tipo descriptionTextLabel. È possibi-le utilizzare diverse soluzioni in questo contesto: po-tremo creare una classe di tipo NSObject e al suo internoinserire due campi di tipo NSString, oppure delle struct,ma per fare qualcosa di diverso dal solito approccioObject Oriented, adopereremo una classe presente in

Fig. 4: Una serie di sezioni con tre righe per ognuna

NOTA

ULTERIORI APPROFONDIMENTIPer approfondire questiconcetti consultare laguida chiamata Table ViewProgramming Guide foriPhone OS disponibile susito developer.apple.com oall'interno delladocumentazione fornitacon XCode.

044-049:088-093-corsi-xsl 29-10-2009 17:33 Pagina 47

Page 45: 86746175 iPhone Programming

45 iPhone programming

iPhone programmingLa gestione delle tabelle negli applicativi Apple iPhoneMOBILE � La gestione delle tabelle negli applicativi Apple iPhone

ht tp ://www. ioprogrammo. i t

G 48 /Novembre 2009

Objective-C disponibile nell’SDK, chiamata NSMuta-bleDictionary. La classe NSMutableDictionary (e lasua versione “statica”, NSDictionary) è un’altra delleclassi rese disponibili dalla libreria Foundation.framework,e permette di creare istanze in cui viene inserito unoo più valori con relative chiavi (ogni coppia viene de-finita come entry), tali chiavi svolgono quindi il com-pito di identificare univocamente un oggetto tra quel-li presenti all’interno dell’NSDictionary, consenten-done l’accesso in maniera estremamente intuitiva.Per realizzare un oggetto di tipo NSMutableDictionaryè necessario inizializzarlo con una serie di chiavi e divalori a queste associati, in corrispondenza uno-a-uno, raggruppati all’interno di due array (di tipo NSAr-ray); gli stati possibili saranno “fatto”, “da fare”, “di-menticato”, mentre il testo, ovviamente, varierà a se-conda dell’azione da effettuare. Scegliere tra NSMu-tableDictionary e la sua controparte immutabile NSDic-tionary segue lo stesso procedimento decisionale uti-lizzato per NSArray; dal punto di vista delle presta-zioni è sicuramente un approccio più pesante rispet-to alla realizzazione di una classe derivata da NSObject,ma in questo contesto è stato un pretesto per intro-durre queste strutture dati e presentare ulteriori clas-si fornite nell’SDK.

NSArray *keys = [NSArray

arrayWithObjects:@"action",@"status",nil ];

NSArray *values = [NSArray

arrayWithObjects:@”Chiamare Simone”,@”Da Fare”,nil];

//Definiamo e inizializziamo il dizionario

NSMutableDictionary *myDict = [[NSMutableDictionary

alloc] initWithObjects:values forKeys:keys]]

In questo modo myDict conterrà due coppie, unacon la chiave “status”, che assumerà in questo caso ilvalore “Da Fare”, mentre l’altra “Chiamare Simone”associata alla chiave “action”. Per accedere ai valoriassociati alle chiavi presenti in myDict basterà utiliz-zare il metodo valueForKey:

//otteniamo lo stato associato (fatto, da fare,dimenticato)

NSString * myStatus = [myDict valueForKey:@"status"];

//otteniamo il task associato (@”Chiamare Simone”)

NSString * myAction = [myDict valueForKey:@"action"];

Ovviamente, un NSMutableDictionary (come ancheNSDictionary) può contenere qualunque oggettoObjective-C e un numero teoricamente infinito dicoppie chiave-valore.

POPOLARE LA TABELLAPer popolare il nostro elenco dovremo prima crearel’array che conterrà le giornate, poiché sarà immuta-bile utilizzeremo un NSArray di sette indici (si ricor-da accessibili da 0 a n-1), all’interno di ognuna di

questi sette indici porremo un NSMutableArray, checi consentirà di variarne il numero di elementi, inostri task, rappresentati come è stato detto daistanze di NSMutableDictionary, nella maniera piùveloce possibile. Dichiariamo giorniSettimana qualearray statico prima del metodo viewDidLoad, e pro-cediamo inizializzandolo:

NSArray *giorniSettimana;

- (void)viewDidLoad {

[super viewDidLoad];

NSLog(@"viewdidload");

self.title = @"ToDo List";

giorniSettimana = [[NSArray alloc]

initWithObjects:[NSMutableArray array],[NSMutableArray

array],[NSMutableArray array],[NSMutableArray

array],[NSMutableArray array],[NSMutableArray

array],[NSMutableArray array],nil ];

//Inseriamo nel giorno di domenica (indice 0) una

serie di azioni

[self insertAction:@"Chiamare Simone"

withStatus:@"da fare" inDay:0];

[self insertAction:@"Comprare Latte"

withStatus:@"dimenticato" inDay:0];

[self insertAction:@"Registrare Telefilm"

withStatus:@"fatto" inDay:0];

//Inseriamo nel giorno di martedì (indice 2) una serie

di azioni

[self insertAction:@"Palestra" withStatus:@"da fare"

inDay:2];

[self insertAction:@"Inviare Email" withStatus:

@"non fare" inDay:2];

[self insertAction:@"Chiamare Gianni"

withStatus:@"dimenticato" inDay:2];

Poiché, come abbiamo affermato precedentemente,NSArray è una struttura dati immutabile, dovremo im-postarne i contenuti in fase di inizializzazione, ciò av-viene invocando il metodo iniWithObjects. Quest’ul-timo accetta un array di oggetti, terminato dal sim-bolo nil: inserendo sette istanze di NSMutableArrayabbiamo così reso questa struttura dinamica nei con-tenuti. Ci sarebbero state differenze sostanziali se aves-simo utilizzato un NSMutableArray per l’elenco deigiorni? Non in questo caso e non in termini di codice,ma sicuramente in termini di prestazioni, quando si ini-zia a lavorare su strutture dati molto più popolate èbuona norma pensare a queste semplici ottimizza-zioni che spesso possono rendere meno oneroso intermini di risorse e prestazioni il vostro software. Come viene affermato dalla documentazione relati-va alla classe, NSArray mantiene un legame strong,forte, con gli oggetti che vengono inseriti al suo inter-no invocando su di essi un retain, e, venendo a cono-scenza di questo comportamento non è stato neces-sario utilizzare la tipica procedura alloc/init ([[NSMu-tableArray alloc] init]) nella fase di inserimento, ab-biamo infatti utilizzato il convenience constructor, un

NOTA

FOUNDATION.FRAMEWORK

All’interno diFoundation.framework

troviamo decine di classi,tra cui NSDictionary eNSMutableDictionary,

NSarray e NSMutableArray,NSString e

NSMutableString, NSDatae NSMutableData, NSDate

e NSError: è probabilmentela libreria che utilizzerete

maggiormente.

G 48 /Dicembre 2009

044-049:088-093-corsi-xsl 29-10-2009 17:33 Pagina 48

Page 46: 86746175 iPhone Programming

iPhone programming 46

iPhone programming La gestione delle tabelle negli applicativi Apple iPhone

ht tp ://www. ioprogrammo. i t

costruttore che non effettua alloc/retain sull’istanzacreata, e che per tale motivo porterà all’autoreleasedei singoli oggetti, ma, poiché questi riceveranno unretain non saranno deallocati. Se avessimo utilizzatoalloc+init, ogni oggetto avrebbe avuto un retainCountpari a due, uno per l’inizializzazione e uno dovuto al-l’inserimento all’interno dell’array, impedendo quin-di la completa deallocazione dell’array quando su diquesto richiameremo il metodo release (gli oggetti alsuo interno avranno un retainCount pari a uno inve-ce che zero, come richiesto per la completa dealloca-zione della struttura dati). Ora che abbiamo creato unarray di sette indici, ognuno contenente un NSMuta-bleArray, realizziamo un semplice metodo per inseri-re all’interno dei giorni i task (che rappresentiamo conNSMutableDictionary):

(void)insertAction:(NSString *)action withStatus:

(NSString *)status inDay:(int)day {

NSArray *values = [NSArray

arrayWithObjects:action,status,nil];

NSArray *keys = [NSArray

arrayWithObjects:@"action",@"status",nil ];

[((NSMutableArray *)[giorniSettimana objectAtIndex:

day]) addObject:[[NSMutableDictionary alloc]

initWithObjects:values forKeys:keys]];

}

Non allarmatevi se riceverete un warning che infor-ma della mancata presenza del metodo, basterà inse-rire la sua signature (-(void)insertAction:(NSString *)ac-tion withStatus:(NSString *)status inDay:(int)day) se-guita da un punto e virgola prima della riga @end al-l’interno del file rootViewcontroller.h. Poiché il nu-mero di utilizzi di questo metodo è ridotto, si è sceltodi non ottimizzare in alcun modo l’utilizzo dell’arraykeys, essendo una struttura che non viene in alcunmodo modificata durante il tempo di vita della nostraapplicazione, potrebbe infatti essere sostituita utiliz-zando la direttiva #define, evitando quindi qualunqueinizializzazione di istanze (in questo caso una per ogniinvocazione del metodo). Il comportamento del me-

todo è abbastanza intuitivo: vengono passate duestringhe che contengono il task e il suo stato, infinel’indice del giorno da utilizzare (anche in questo ca-so si potevano usare i #define per rendere il codice piùleggibile all’interno di viewDidLoad); viene prelevatol’oggetto di giorniSettimana presente in posizione i-esi-ma, effettuato il casting a NSMutableArray per con-sentire al sistema di code completition di mostrarcisolo i metodi coerenti con tale classe (infatti objectA-tIndex restituisce un’istanza di tipo id che rende inu-tilizzabile tale funzionalità, ne avevamo parlato in unodei primi articoli dedicati alla programmazione iPho-ne) e inserito al suo interno un NSMutableDictionarycontenente le due coppie con chiavi action e status.

AGGIORNAMENTICosa ci resta da fare? Per come abbiamo strutturato inostri dati identificheremo con le sezioni i singoli gior-ni della settimana, mentre le righe saranno i singoliNSMutableDictionary in esse presenti. Prima dob-biamo modificare il valore restituito da tableview:numberOfRowsInSection: in modo che fornisca il cor-retto numero di righe presenti nella sezione/giorno.Basterà invocare il metodo count su ogni singolo og-getto del nostro array:

- (NSInteger)tableView:(UITableView *)tableView

numberOfRowsInSection:(NSInteger)section {

return [((NSMutableArray *)[giorniSettimana

objectAtIndex:section]) count]; }

Per popolare la tabella basterà ottenere le informa-zioni action/status per ogni NSMutableDictionary pre-sente nelle varie sezioni/giorni:

- (UITableViewCell *)tableView:(UITableView *)tableView

cellForRowAtIndexPath:(NSIndexPath *)indexPath {...

NSString *action = [(NSMutableDictionary

*)[((NSMutableArray *)[giorniSettimana

objectAtIndex:indexPath.section])

objectAtIndex:indexPath.row] valueForKey:@"action"];

NSString *status = [(NSMutableDictionary *)[((NSMuta

bleArray *)[giorniSettimana

objectAtIndex:indexPath.section])

objectAtIndex:indexPath.row] valueForKey:@"status"];

cell.textLabel.text = action;

cell.detailTextLabel.text = status;

return cell; }

CONCLUSIONINel prossimo articolo introdurremo altri concetti etecniche per utilizzare la tabella, parleremo di can-cellazioni, inserimenti e altre operazioni.

Andrea Leganza

Novembre 2009/ 49 G

La gestione delle tabelle negli applicativi Apple iPhone � MOBILE

Dicembre 2009/ 49 G

L’AUTORE

Andrea LeganzaLaureato in IngegneriaInformatica, da oltre undecennio realizza soluzionimultimediali su piattaformee con linguaggi eterogenei.Certificato Adobe ACE -Adobe Flex 3 and AIRCertified Expert, e EUCIPCore, appassionato difotografia, linguagiapponese e istruttore dinuoto 2° Livello FIN, èattualmente impegnato innumerosi progettimultimediali, anche coniPhone, con alcune societànazionali ed internazionali;è contattabile [email protected] odirettamente sul sitowww.leganza.it.

Fig. 5: Ecco come sono state strutturate le informazio-ni che vogliamo gestire

044-049:088-093-corsi-xsl 29-10-2009 17:33 Pagina 49

Page 47: 86746175 iPhone Programming

47 iPhone programming

iPhone programmingLe tecniche per inserire e cancellare righe da una tabella

GESTIONE DELLECELLE NELLE TABELLEIN QUESTO ARTICOLO TRATTIAMO DELLE VARIE FUNZIONALITÀ OFFERTE DALL’SDK PERINSERIRE E CANCELLARE UNA O PIÙ RIGHE NELLE TABELLE: ELEMENTO FONDAMENTALE NELLACOSTRUZIONE DELLE INTERFACCE, SIA PER L’INPUT CHE PER LA VISUALIZZAZIONE DEI DATI

Nell’articolo precedente abbiamo primamostrato come si popola una tabella utiliz-zando una struttura statica, un array, e succes-

sivamente abbiamo realizzato un metodo per popo-larne una utilizzando una struttura dinamica (di tipoNSMutableArray):

-(void)insertAction:(NSString *)action

withStatus:(NSString *)status inDay:(int)day {

NSArray *values = [NSArray

arrayWithObjects:action,status,nil];

NSArray *keys = [NSArray

arrayWithObjects:@"action",@"status",nil ];

[((NSMutableArray *)[giorniSettimana object

AtIndex:day]) addObject:[[NSMutableDictionary

alloc] initWithObjects:values forKeys:keys]];

}

Poiché il metodo che abbiamo mostrato non èinvocabile, in risposta alla pressione di un pulsanteo di un altro componente, non essendo di tipoIBAction (che ricordiamo essere un tipo di metodoche non accetta altri parametri se non il compo-nente grafico che lo ha invocato), dobbiamo per-correre un’altra strada per poter consentire confacilità l’esecuzione di una qualunque modifica inrisposta all’interazione dell’utente: esistono pernostra fortuna svariate tecniche per effettuare ciò,una di queste consiste nel modificare la strutturadella tabella fornendo campi di testo e pulsantinecessari per modificare i contenuti della strutturadati che utilizziamo, e un’altra invece si realizzapresentando un ulteriore UIViewController conrelativa UIView, potremo anche presentare unAlertView customizzato; utilizzeremo questa terzaprocedura per aggiungere un nuovo evento.

LA NAVIGAZIONE TRA “SCHEDE”L’utilizzo dell’iPhone è principalmente basatosul concetto di consultazione progressiva diinformazioni, come è stato spiegato quando

abbiamo trattato dei passaggi necessari per arri-vare alla scheda di dettaglio di una serie di tabel-le. La navigazione è una funzionalità fornita dalcomponente che contiene la nostra tabella rea-lizzata nel file RootViewController.h/.m e nel rela-tivo file .xib, di tipo UINavigationController: seandiamo infatti ad esplorare le variabili presentinel file nomeprogettoAppDelegate.h possiamonotare che ne è presente una di tipoUINavigationController, di tipo IBOutlet, chia-mata navigationController, che nel corpo delmetodo applicationDidFinishLaunching, è uti-lizzata dal contenitore principale, la variabilewindow, come fonte della view da mostrare aschermo. In pratica, cosa viene realizzato auto-maticamente quando si crea un progetto di que-sto tipo navigation-based? La UIWindow contie-ne al suo interno una istanza di unUINavigationController, che a sua volta includeuna tabella; la presenza del navigation controllerci permette con estrema facilità di gestire inseri-menti, e ovviamente rimozioni, di un numerovirtualmente illimitato di ulterioriUIViewController (nel cui interno sono presentiquindi UIView): in questo modo è possibile rea-lizzare un complesso sistema di navigazione, chepotremo dire “a schede” (con il quale intendiamoil binomio UIViewController e relativo UIView).Riguardo l’UINavigationController, oltre a gesti-re la navigazione, consente di inserire dei pul-santi nella sua barra superiore, chiamata naviga -tionBar. Parleremo di questi aspetti successiva-mente, ma era necessario presentare almenoqueste minime informazioni perché l’UINaviga -tion Controller verrà utilizzato quando dovremoimplementare i pulsanti per effettuare le cancel-lazioni e gli inserimenti degli eventi.

NSSTRINGPrima di iniziare è doveroso trattare delle strin-ghe. In uno degli articoli precedenti, dove ave-vamo trattato della gestione della memoria, e

MOBILE � Le tecniche per inserire e cancellare righe da una tabella

ht tp ://www. ioprogrammo. i t

G 40 /Gennaio 2010

❑ CD ❑ WEBiphone_tabella146.zip

cdrom.ioprogrammo.it

Conoscenze richiesteOOP

SoftwareMacOS X 10.5.4 osuperiore, XCode

Impegno

¥Tempo di realizzazione

REQUISITI

040-045:088-093-corsi-xsl 1-12-2009 16:25 Pagina 40

Page 48: 86746175 iPhone Programming

iPhone programming 48

iPhone programming Le tecniche per inserire e cancellare righe da una tabella

ht tp ://www. ioprogrammo. i t Gennaio 2010/ 41 G

Le tecniche per inserire e cancellare righe da una tabella � MOBILE

degli utilizzi di retain/release avevamo eviden-ziato che le stringhe del tipo NSString non eranosoggette a queste regole, avevamo infatti utiliz-zato negli esempi istanze di NSMutableString; èormai necessario aprire una parentesi e spiega-re perché NSString si esenta dalla gestione dellememoria dinamica, soprattutto perché è unodei tipi di dato maggiormente utilizzati negliapplicativi iPhone, dove spesso si richiede all’u-tente di compilare uno o più campi di testo. Laspiegazione è di estrema facilità: ogni istanza diNSString è sempre gestita come una costanterappresentata in Unicode, e non vi è quindimodo di modificare il contenuto di una variabi-le di questo tipo senza che venga, esplicitamen-te o implicitamente, sostituita con una nuova;se andassimo ad analizzare come vengonogestite due variabili in memoria il cui testo èidentico, ad esempio “prova”, scopriremo chequeste due puntano alla stessa locazione dimemoria, indifferentemente dal momento incui le abbiamo istanziate, e alla classe in cui taleoperazione avviene: lo scopo di tale comporta-mento è ovviamente quello di ottimizzare ilconsumo di memoria, infatti in questo modo sipossono avere migliaia di alias di un solo testo(n alias diversi e una sola variabile in memoria),e non migliaia di istanze con contenuto identi-co (n variabili in memoria) la cui occupazione èquindi di tipo lineare. Quando, durante la fasedi compilazione, verranno analizzati i sorgenti,verrà avviato un processo che come risultatofarà in modo che tutte le variabili con stessocontenuto punteranno ad una sola locazione dimemoria. Essendo quindi le stringhe dellecostanti vengono esentate dalla necessità diretain/release/autorelease e pertanto non ci sideve mai preoccupare di invocare tali metodiper gestirne la memoria. Ultima nota riguar-dante le stringhe: queste consentono di utilizza-re il simbolo di doppia uguaglianza per verifica-re se i loro caratteri sono identici, anche sel’operatore == non ha subito overloading, que-sto è dovuto al fatto che tale operatore verifica,per default, se i due indirizzi puntati da duevariabili sono identici, e come appena spiegatociò è sempre vero se due stringhe hanno ugualicaratteri.

NSString *myString = @”prova”;

NSString *myString2 = @”prova”; //punteranno in

memoria alla stessa locazione

if (myString==myString2) {codice sempre

eseguito}

Ciò non è vero quando utilizziamo dueNSMuta ble String, in questo caso l’uguaglianzanon avrà l’esito sperato perché le due variabili

puntano sempre a locazioni diverse; in questocaso è quindi necessario utilizzare il metodoisEqualTo String.

NSMutableString *myString = [NSMutableString

stringWithString:@"prova"];

NSMutableString * myString2 = [NSMutableString

stringWithString:@"prova"];

if (myString == myString2) NSLog(@"sono uguali");

//non viene mai verificata come condizione

if ([myString isEqualToString: myString2])

NSLog(@"sono uguali con equaltostring");

Anche se è quindi lecito utilizzare l’== con lestringhe di tipo NSString si rischia, in caso didimenticanza, di utilizzarlo anche con le NSMu ta bleString, o peggio, con istanze di altri oggetti:il consiglio è quindi di usare isEqualto String perevitare possibili errori.Poiché dovremo prelevare del testo da uno o piùcampi, non avere questa consapevolezzapotrebbe creare problemi inizialmente a queilettori meno attenti/preparati, inducendoli apassare ore cercando di deallocare queste varia-bili senza successo ottenendo crash continui.

I POSSIBILI STATIDELLE CELLE DI UNATABELLAOgni cella di una tabella si comporta come unautoma a stati finiti, può passare quindi tra unnumero limitato di diverse configurazioni,ognuna delle quali generalmente condiziona ilsuo aspetto. Successivamente, se coerente con ilcomportamento atteso, anche il contenutodella relativa “riga” associata nella struttura dati

NOTA

NSSTRINGOgni stringa di tipoNSString è una costante,quindi non è soggetta adalcuna gestione da partedell’utente e neppure dalsistema di gestioneautomatica delle risorse;inoltre tutte quelle stringheche hanno identico valorepuntano ad una stessalocazione di memoria.

Fig. 1: Due stringhe con ugual contenuto di testo punteranno alla stessa locazione inmemoria. Nel nostro esempio: var1 e var2

040-045:088-093-corsi-xsl 1-12-2009 16:25 Pagina 41

Page 49: 86746175 iPhone Programming

49 iPhone programming

iPhone programmingLe tecniche per inserire e cancellare righe da una tabellaMOBILE � Le tecniche per inserire e cancellare righe da una tabella

ht tp ://www. ioprogrammo. i tG 42 /Gennaio 2010

collegata: abbiamo uno stato “normale” duran-te il quale si effettuano le semplici operazioni discorrimento e di selezione delle “righe”(UITableView CellEditingStyleNone), poi unostato di “modifica” che può essere di cancella-zione, oppure di inserimento (esiste anche unterzo, quello di riordino ma che non viene uti-lizzato in questo modo), il primo prende ilnome di UITableViewCellEditing StyleDelete,mentre il secondo UITableViewCell Edi ting -StyleInsert: tali “stati” sono quindi degli indica-tori per consentire di valutare se una determi-nata cella è al momento, visivamente parlando,in uno stato di modifica oppure no; esiste inol-tre una proprietà della cella, editing, di tipobooleano, il cui scopo è indicare l’effettivo statodella cella, teoricamente sarebbe infatti possibi-le impostare una cella con uno stile visivo dimodifica e renderla invece non editabile; gene-ralmente editing non viene consultata perché cisi affida al fatto che una cella con un determi-nato stile ha già automaticamente impostatotale valore a true o false; se si utilizzano i meto-di relativi alla tabella, quelli che vengono gene-rati automaticamente dal wizard, non avretenecessità di valutarla. Infatti noteremo nel pros-simo paragrafo che semplicemente verrà con-sultato lo stile visuale e non editing.Generalmente lo stato di cancellazione si evi-denzia in due modi: o si presenta con un pul-sante rosso, simile ad un segnale di stop, sul latosinistro della cella che, quando premuto, faràcomparire un bottone sul lato destro della cellaper confermare tale operazione, oppure consolamente un bottone sulla destra; questosecondo aspetto si presenta quando si effettuala cancellazione effettuando lo scorrimento deldito da destra verso sinistra sulla cella desidera-ta. Molti metodi da utilizzare per gestire talioperazioni vengono generati automaticamentedal wizard del progetto, basterà decommentarlie personalizzarli a seconda delle proprie neces-sità.

GESTIRE LECANCELLAZIONIGestire la cancellazione di una o più celle,quando è necessario rispondere all’interazionecon l’utente, si rivela per nostra fortuna,un’ope razione estremamente semplice; perquanto riguarda l’utilizzatore dell’interfaccia,tale operazione viene invocata automatica-mente quando si scorre il dito sulla cella conun movimento da destra verso sinistra, oppurepremendo un pulsante, generalmente presentein alto a destra, con label edit, che mostrerà

successivamente un pulsante rosso sul latosinistro di ogni riga; dal punto di vista del pro-grammatore, per mostrare il pulsante di edit inalto a destra, basterà inserire la seguente rigaall’interno del metodo viewDidLoad, ovvia-mente dopo la chiamata al metodo della classepadre:

[super viewDidLoad];

self.navigationItem.rightBarButtonItem =

self.editButtonItem;

In questo modo si è impostato il pulsantedestro presente nella navigationBar delUINavigation Controller con uno predefinito,presente in tutti gli UIViewController, il cuicomportamento, preimpostato, è quello diinvocare il metodo (void)setEditing:(BOOL)editinganimated:(BOOL)animated (comeespli ci tamente descritto nelle documentazio-ne di UIViewController), per la UITableViewquesto metodo è stato già implementato inter-namente alla classe e non sarà necessariocrearlo o modificarlo: provvederà ad iterare pertutte le righe e mostrare il pulsante rosso sullato sinistro di ognuna di queste (se è abilitataalla cancellazione). Il pulsante è, per informa-zione, appartenente alla classe UIBar But ton -Item e, per supportare la creazione di una cellanel prossimo paragrafo, la utilizzeremo peristanziare un bottone ad hoc (quello nellaparte sinistra). Abbiamo ora reso disponibilidue modalità di cancellazione, quella multipla,il cui utilizzo termina con l’ulteriore pressionedel pulsante presente a destra nellanavigationBar, che assume il testo “done” suc-cessivamente alla prima interazione, e quellasingola, scorrimento destra-sinistra del ditosulla cella. Ora è necessario abilitare la cancellazione diuna o più celle: cosa che si realizza implemen-tando un metodo per effettuare una o più can-cellazioni. Per supportare questa operazione,basterà decommentare, il metodo che riportia-mo di seguito:

-(void)tableView:(UITableView*)tableView

commitEditingStyle:(UITableViewCellEditingStyle)

editingStyle forRowAtIndexPath:(NSIndexPath

*)indexPath {

if (editingStyle ==

UITableViewCellEditingStyleDelete)

{

// Delete the row from the data source.

//[tableView deleteRowsAtIndexPaths:[NSArray

arrayWithObject:indexPath]

withRowAnimation:UITableViewRowAnimationFade];

NOTA

UGUAGLIANZA TRA STRINGHE

L’utilizzo di == qualeoperatore per verificare

l’uguaglianza tra duestringhe di tipo NSString

funziona, nonostante nonsia soggetto ad

overloading, poiché verificase i due indirizzi sono

identici. Situazione veraquando due stringhe

puntano ad una stessalocazione di memoria, e ciò

avviene perché stringhecon contenuti ugualipuntano alla stessa

memoria.

040-045:088-093-corsi-xsl 1-12-2009 16:25 Pagina 42

Page 50: 86746175 iPhone Programming

iPhone programming 50

iPhone programming Le tecniche per inserire e cancellare righe da una tabella

ht tp ://www. ioprogrammo. i t Gennaio 2010/ 43 G

Le tecniche per inserire e cancellare righe da una tabella � MOBILE

}

}

Decommentando questo metodo si rendonocancellabili tutte le celle; se si volesse creare lalogica per proteggerne una o più si dovràdecommentare il seguente metodo, sempregenerato dal wizard del progetto, e decidere, aseconda della cella ricevuta, se restituire YESoppure NO:

- (BOOL)tableView:(UITableView *)tableView

canEditRowAtIndexPath:(NSIndexPath *)indexPath

{

// Return NO if you do not want the specified

item to be editable.

return YES;

}

Tornando al metodo utilizzato per modificarela tabella, analizzando il comportamento didefault fornito dagli ingegneri Apple, si noteràche viene prima verificato lo stile attuale dellacella sulla quale tale metodo è stato invocato e,se corrisponde a UITableViewCellEditing -StyleDelete, si può provvedere a cancellare la“riga” presente nella nostra tabella. La parte dicodice che quindi dovremo modificare è quel-la precedente alla seguente riga (che dovretedecommentare se fosse commentata):

[tableView deleteRowsAtIndexPaths:[NSArray

arrayWithObject:indexPath]

withRowAnimation:UITableViewRowAnimationFade];

Questa chiamata provvede a cancellare visiva-mente la cella, infatti viene invocata sullanostra tabella, tableView, ma se non provvede-remo a rimuovere anche la relativa entità nellanostra struttura dati avremo un’incoerenza tramodel (la struttura dati usata, NSMutableArray) e view (le celle della tabella), il nostrosoftware verrà terminato con un crash moltoesplicativo questa volta:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (3) must be equal to the number of rows contained in that section before the update (3), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted).

Basterà quindi aggiungere la seguente riga

prima delle cancellazione della cella dellatabella:

//Cancellazione dal MODEL

[((NSMutableArray *)[giorniSettimana

objectAtIndex:indexPath.section])

removeObjectAtIndex:indexPath.row];

//Cancellazione dal VIEW

[tableView deleteRowsAtIndexPaths:[NSArray

arrayWithObject:indexPath]

withRowAnimation:UITableViewRowAnimationFade];

Come si nota la gestione della cancellazione siè rivelata un’operazione molto semplice: hacomportato solo decommentare il metodorelativo e l’aggiunta di una sola riga per farriflettere questa modifica anche alla strutturadati.

GESTIRE GLIINSERIMENTIEsistono svariati metodi per aggiungere una“riga” nella nostra tabella, come abbiamodetto in precedenza, in questo tutorial utiliz-zeremo un UIAlertView customizzato;l’UIAlertView è una UIView che generalmente,in maniera modale, quindi impedendo intera-

RIFERIMENTI WEBCreazione dell'account, perscaricare l'SDK econsultare ladocumentazione:http://developer.apple.com/iphone/

NOTA

Fig. 2: Ecco come si presenta la modalità di cancellazione multipla

040-045:088-093-corsi-xsl 3-12-2009 11:34 Pagina 43

Page 51: 86746175 iPhone Programming

51 iPhone programming

iPhone programmingLe tecniche per inserire e cancellare righe da una tabellaMOBILE � Le tecniche per inserire e cancellare righe da una tabella

ht tp ://www. ioprogrammo. i t

G 44 /Gennaio 2010

zione con gli altri componenti, presenta uno odue, bottoni mostrando un messaggio di testoe richiedendo una scelta: un esempio tipico èil messaggio che richiede di autorizzare laconsultazione della posizione del telefono peruna qualche funzionalità di localizzazioneche si trova in numerosi applicativi. Noi lomodificheremo per le nostre necessità utiliz-zando delle funzioni nascoste, non pubblica-mente rese disponibili da Apple, che possonoessere utilizzate per inserire un qualunquenumero di campi di testo (UITextField) al suointerno. Dovremo effettuare tre operazioni, la primaconsiste nell’inserire un pulsante nellanavigationBar sul lato sinistro che ci consen-ta di inserire una nuova scheda, presentando-ci quindi l’UIAlertView; la seconda è imple-mentare il metodo addItem, che provvederà acreare l’UIAlert View; la terza saràl’implementazione di un secondo metodo inrisposta alla conferma della creazione dell’e-vento, successivamente alla pressionedell’UIAlertView. Per poter effettuare la prima operazione,quella di visualizzazione del pulsante sullasinistra, provvediamo aggiungendo le seguen-ti righe sempre nel metodo ViewDidLoad,immediatamente dopo il comando che abbia-mo utilizzato per mostrare il pulsante di can-cellazione:

self.navigationItem.rightBarButtonItem =

self.editButtonItem;

UIBarButtonItem *addButton =

[[UIBarButtonItem

alloc] initWithBarButtonSystemItem:UIBarButton

SystemItemAdd target:self

action:@selector(addItem:)];

self.navigationItem.leftBarButtonItem =

addButton;

[addButton release];

Il codice è molto semplice: si crea un’istanzadi un bottone, come una normale istanza diun oggetto, lo si imposta con uno stile visualeche mostra il simbolo di più (+) ad indicarel’inserimento, e si setta come target responsa-bile della gestione degli eventi il metodoaddItem nella stessa classe in cui ci troviamo(self sta proprio ad indicare di utilizzare que-sta classe). Dopo aver associato il pulsante di sinistra,leftBar ButtonItem, con il nostro bottone,dovremo ricordarci di rilasciare la risorsa,

questo è dovuto al fatto che, avendo utilizzatoalloc/init, avremo un retainCount di 1,l’assegnazione del pulsante di sinistra, comesegnalato dalla documentazione, provvede adeffettuare un retain. Quindi, al termine del metodo, se non effet-tuassimo un release, avremo un retainCountdi 2, che non verrebbe mai deallocato, neppu-re nel caso rilasciassimo il UIViewNavigator,diventando quindi leak. Passiamo ora allaseconda fase:

- (void)addItem:sender {

UIAlertView *alert = [[[UIAlertView alloc]

initWithTitle:@"Create new Event" message:@""

delegate:self

cancelButtonTitle:@"Cancel"

otherButtonTitles:@"Submit", nil

] autorelease];

[alert addTextFieldWithValue:@"Sunday"

label:@"Day of the week"];

[alert addTextFieldWithValue:@"" label:@"What"];

[alert addTextFieldWithValue:@""

label:@"Status"];

[alert show]}

L’implementazione del metodo addItem creaun UIAlertView con titolo “Create new Event”,impostando due bottoni, uno di annullamen-to (con label Cancel) e uno di conferma (conlabel Submit); entra ora in gioco l’utilizzo diun metodo non pubblicato da parte di Appleassociato alla classe UIAlertView, tale metodoaddText FieldWithValue, consente di inserireun numero illimitato di campi di testo checonsulteremo quando confermeremo la crea-zione dell’evento. Per accedere a questi campi di testo dovremoutilizzare un altro metodo nascosto,[alertView textFieldAtIndex:i], dove ovviamen-te i è l’indice, partendo da 0, a cui è associatoogni campo, 0 per il campo del giorno dellasettimana, 1 per quello dell’azione, 2 per lostato dell’azione. Per quanto riguarda la pressione dei due pul-santi disponibili implementiamo il metodoalertView: clickedButton AtIndex, invocatoautomaticamente quando premeremo cancelo submit.

- (void)alertView:(UIAlertView *)alertView

clickedButtonAtIndex:(NSInteger)buttonIndex

{

if (buttonIndex !=

[alertView cancelButtonIndex])

{

NOTA

UIALERTVIEWUn componente di tipo

UIAlertView consente dipresentare messaggi, con una o più possibili

scelte, in maniera modale,intercettando quindi

l’interazione dell’utente.

NOTA

ULTERIORIAPPROFONDIMENTI

Per approfondire questiconcetti consultare la

guida chiamata Table ViewProgramming Guide for

iPhone OS disponibile susito developer.apple.com

o all'interno delladocumentazione fornita

con XCode.

040-045:088-093-corsi-xsl 1-12-2009 16:25 Pagina 44

Page 52: 86746175 iPhone Programming

iPhone programming 52

iPhone programming Le tecniche per inserire e cancellare righe da una tabella

ht tp ://www. ioprogrammo. i t

short day = 0;

if ([((UITextField *)[alertView textFieldAtIndex:0])

.text isEqualToString:@"Sunday"]) day = 0;

else if ([((UITextField *)[alertView

textFieldAtIndex

:0]).text isEqualToString:@"Monday"]) day = 1;

else if ([((UITextField *)[alertView

textFieldAtIndex

:0]).text isEqualToString:@"Tuesday"]) day = 2;

else if ([((UITextField *)[alertView

textFieldAtIndex

:0]).text isEqualToString:@"Wednesday"]) day =

3;

else if ([((UITextField *)[alertView

textFieldAtIndex

:0]).text isEqualToString:@"Thursday"]) day = 4;

else if ([((UITextField *)[alertView

textFieldAtIndex

:0]).text isEqualToString:@"Friday"]) day = 5;

else if ([((UITextField *)[alertView textFieldAt

Index:0]).text isEqualToString:@"Saturday"]) day

= 6;

[self insertAction:((UITextField *)[alertView

textFieldAtIndex:1]).text withStatus:((UITextField

*)[alertView textFieldAtIndex:2]).text inDay:day];

[(UITableView *)self.view reloadData]; //

//oppure [tableView reloadData] se tableView è il

nome dell’IBOutlet della tabella

}

}

Come prima operazione verifichiamo che nonsia stato richiesto l’annullamento dell’opera-zione, basterà verificare che buttonIndex,parametro passato dall’UIAlertView, corri-spondente all’indice del pulsante premuto,non sia quello associato al tasto cancel; effet-tuiamo successivamente una semplice ricercasul giorno della settimana inserito dall’utentenel primo campo di testo (indice = 0), come sipuò notare è stato utilizzato il casting su[alertViewtextFieldAtIndex:0].Quando abbiamo identificato il giorno, potre-mo finalmente aggiungere la nuova azione nelcorretto giorno della settimana con il relativostato. Anche in questo caso avremmo potutoutilizzare l’operatore di doppia uguaglianzaper verificare quale giorno era stato digitato.Operazione finale, e comunque obbligatoria,consiste nel notificare la View, la nostra tabel-la, che è avvenuta una modifica del Model, ilnostro array. Riceverete numerosi warning in fase di com-pilazione, questo è dovuto al fatto che stiamousando metodi non resi pubblici dalla classeUIAlertView: ricordiamo che non è possibilerendere privato un metodo ma solo nascon-derlo con alcuni artifici del linguaggio, quindinon c'è modo di impedirne effettivamentel'invocazione.

Andrea Leganza

Gennaio 2010/ 45 G

Le tecniche per inserire e cancellare righe da una tabella � MOBILE

L’AUTORE

Andrea LeganzaLaureato in IngegneriaInformatica, da oltre undecennio realizza soluzionimultimediali, e non, supiattaforme e con linguaggidiversi. Certificato AdobeACE - Adobe Flex 3 and AIRCertified Expert, e EUCIPCore, appassionato difotografia, linguagiapponese e istruttore dinuoto FIN, è attualmenteimpegnato in numerosiprogetti multimediali,anche con iPhone, conalcune società nazionali edinternazionali; ècontattabile [email protected] odirettamente sul sitowww.leganza.it.

Fig. 3: L’UIAlertView che mostra i campi di input Fig. 4: Il risultato dell’inserimento

040-045:088-093-corsi-xsl 1-12-2009 16:25 Pagina 45

Page 53: 86746175 iPhone Programming

53 iPhone programming

iPhone programming Timer e suoni: realizziamo un progetto completo

UNA SVEGLIA DIGITALE PER IPHONEIN QUESTO ARTICOLO MOSTRIAMO COME REALIZZARE UNA SVEGLIA DIGITALE CHE CI AVVISERÀ DI IMPEGNI E SCADENZE IMMINENTI. SARÀ L’OCCASIONE DI APPROFONDIRE I CONCETTI LEGATI ALLA GESTIONE DELL’INTERFACCIA E DEL TIMER

In questo articolo mostreremo come realizza-re una sveglia digitale. Grazie a questo pro-getto avremo modo di introdurre alcune clas-

si di estrema utilità: NSTimer, fornita dal fra-mework Foundation, che insieme a UIKit rappre-senta le fondamenta di tutte le applicazioni periPhone, e AVAudioPlayer, presente nel frameworkAVFoundation, il cui scopo è consentirel’esecuzione di brani audio di “qualunque”dimensione. Poiché, come è risaputo, non è per-

messo nell’iPhone mantenere un’applicazionein background, si potrà utilizzare questa funzio-nalità di sveglia solo quando la nostra applica-zione è effettivamente in esecuzione.

IL TIPO DI PROGETTOPer realizzare questo software utilizzeremo unnuovo tipo di progetto fornito da XCode chiama-to Utility Application: gli applicativi che vengonoidentificati con questo termine sono costituiti dadue UIViewController contenenti una singolaUIView ciascuno, che si alternano in base allapressione di un preciso tasto. Il viewcontrollerprincipale viene chiamato automaticamenteMainViewController. Il passaggio dall’una a l’altra schermata avvienecon la pressione del pulsante i nel MainView, econ l’utilizzo di quello con label done, posiziona-to in alto a sinistra nella barra di navigazione diFlipsideViewController. Il sistema utilizzato pereffettuare lo switch è relativamente complesso enon ne parleremo in questo articolo. Essendo laterza serie di articoli prenderemo per scontatetutte le pratiche necessarie per effettuare la crea-zione degli IBOutlet e per prelevare le informazio-ni da tali oggetti. L’intera business logic verràimplementata all’interno del MainViewController, nel quale abbiamo accesso anche aicontenuti del FlipsideViewController.

IL COMPONENTEUIDATEPICKERInserendo un componente del tipo UIDatePickerall’interno della nostra interfaccia grafica, nelFlipsideViewController, forniremo all’utente lapossibilità di selezionare una data, con una pre-cisione a nostra discrezione; in questo casoimpostiamo, tramite Interface Builder, il formatocompleto (giorno, mese, anno, ore, minuti) set-tando mode al valore “Date and Time”, la lingua

MOBILE � Timer e suoni: realizziamo un progetto completo

ht tp ://www. ioprogrammo. i t

G 40 /Febbraio 2010

❑ CD ❑ WEBsveglia.zip

cdrom.ioprogrammo.it

Conoscenze richiesteOOP

SoftwareMacOS X 10.5.4 osuperiore, XCode

Impegno

¥Tempo di realizzazione

REQUISITI

Fig. 1: Al termine del progetto avremo realizzato unasveglia digitale

040-045:088-093-corsi-xsl 29-12-2009 16:53 Pagina 40

Page 54: 86746175 iPhone Programming

iPhone programming 54

iPhone programming Timer e suoni: realizziamo un progetto completo

ht tp ://www. ioprogrammo. i t Febbraio 2010/ 41 G

Timer e suoni: realizziamo un progetto completo � MOBILE

italiana utilizzando il campo locale, e la precisio-ne, con il campo interval, ad 1 minuto.UIDatePicker fornisce un metodo per ottenere ladata selezionata, questa è un’istanza della classeNSDate e, quando torneremo al MainView prov-vederemo a memorizzarla in una cartelladell’iPhone, ed utilizzarla per verificare sel’allarme dovrà essere eseguito.

CONTROLLO NSTIMERUn timer è un sistema che permette di cadenza-re l’esecuzione di un metodo con precisi inter-valli; ogni timer viene eseguito in un threaddistinto. Attenzione, un NSTimer non garantisceche l’esecuzione del metodo associato avvengasempre in un preciso istante, ma che avverrà inun istante pari o successivo a quello desiderato,questa limitazione è dovuta al fatto che, trovan-doci in un ambiente multithread e multiprocess,dovremo condividere le risorse hardware conaltri processi in esecuzione. In genere ciò avvieneutilizzando uno dei cosiddetti “algoritmi di sche-duling”, i quali provvedono a fornire in manieraciclica sufficiente tempo di calcolo sulla CPU atutti i processi che ne facciano richiesta; potreb-be capitare quindi che, quando il nostro timer siain procinto di scadere, avvenga un evento fuoridal nostro controllo che interrompa o rallentil’esecuzione del nostro software per alcuni milli-secondi, impedendogli di effettuare in tempol’esecuzione del metodo. La precisione dichiara-ta è di circa 50/100 millisecondi, ma è comunquenon garantita per le motivazioni appena citate.

NSTimer *timer = [NSTimer

scheduledTimerWithTimeInterval: 1

target: self

selector: @selector(handleTimer:)

userInfo: nil

repeats: YES];

- (void) handleTimer: (NSTimer *)

timer{ metodo invocato da NSTimer}

Con questo codice abbiamo realizzato un timerche viene avviato ogni secondo (o nei millesimisuccessivi), che allo scadere dell’intervallo ese-gue il metodo handleTimer e che si ripete all’infi-nito. Il metodo viene immediatamente avviatodopo la sua esecuzione e subisce un retain auto-matico. Quando, (e se), non avremo più bisognodi questa istanza basterà invocare su di essa ilmetodo invalidate, che provvederà ad effettuaresu di essa un release automatico. Per il nostroscopo questo metodo dovrà verificare se è giuntoil momento per eseguire l’audio dell’allarme,operazione effettuabile semplicemente confron-

tando la data attuale, ottenuta richiedendo ilvalore del campo date dalla classe NSDate, conquella prelevata dall’UIDatePicker:

- (void) handleTimer: (NSTimer *) timer {

if ([alarmDate timeIntervalSinceNow]<=0)

{

//viene eseguito il suono dell’allarme

}

}

timeIntervalSinceNow restituisce un valore positi-vo, i secondi mancanti alla data attuale, quandoalarmDate è una data futura, mentre i valori sononegativi se alarmDate è ormai trascorso. Abbiamo dovuto utilizzare sia l’uguaglianza cheil simbolo di minore uguale perché, come è statodetto, non si può prevedere se il timer verrà ese-guito nel preciso minuto in cui dovrebbe scattarel’evento, o in uno dei successivi.

LA MEMORIZZAZIONEDELL’ORA DELL’ALLARMEDopo aver selezionato una data adoperandol’UIDatePicker, provvederemo a memorizzarla inuna cartella locale all’iPhone e a caricarla ognivolta che il nostro applicativo verrà eseguito.All’interno del telefono esistono alcune cartelleliberamente accessibili e modificabili nelle qualipotremo salvare qualunque tipo di informazio-ne: tmp e Documents. La prima deve essere utiliz-zata per creare e gestire informazioni il cuitempo di vita è limitato alla singola esecuzione,mentre la seconda per tutti quei casi in cui undato deve permanere per diversi avvii dell’appli-cativo. Esistono svariati metodi per ottenere lacorretta posizione di queste cartelle, che sonouniche per ogni software, utilizzeremo il metodoconsigliato da Apple quando si realizza un pro-getto che utilizza la tecnologia Core Data (dellaquale tratteremo in un prossimo articolo) :

- (NSString *)applicationDocumentsDirectory {

return

[NSSearchPathForDirectoriesInDomains(NSDocument

Directory, NSUserDomainMask, YES) lastObject];

}

Questo codice restituisce il path, una stringa cherappresenta la posizione completa della cartellaDocuments. Questo valore potrà assumere valoridiversi a seconda dell’applicativo, ma anche se vitroverete a utilizzare il tutto nel simulatore. Ii questo caso punterà ad una sottocartella defi-nita in: /Users/$NOMEUTENTE/Library/ApplicationSupport/iPhoneSimulator/User/Applications/, oppu-

Fig. 3: Il FlipsideViewController conl’UIDatePicker

Fig. 2: Il MainViewController che mosteràl’allarme

040-045:088-093-corsi-xsl 31-12-2009 11:46 Pagina 41

Page 55: 86746175 iPhone Programming

55 iPhone programming

iPhone programmingTimer e suoni: realizziamo un progetto completoMOBILE � Timer e suoni: realizziamo un progetto completo

ht tp ://www. ioprogrammo. i tG 42 /Febbraio 2010

re nel telefono. Per creare un path completocomprensivo del nome del file basterà comporreuna stringa utilizzando la seguente procedura:

NSString *archivePath = [[self

applicationDocumentsDirectory]

stringByAppendingPathComponent:@"sveglia.cfg"];

Con questa riga abbiamo realizzato con estremasemplicità un path per un file chiamato sveg-lia.cfg che verrà salvato e caricato quando richie-sto. Per verificare se il file sveglia.cfg esiste giàall’interno della cartella Documents, basteràsemplicemente utilizzare il seguente metodo,fileExistsAtPath, fornito dalla classe NSFile -Manager, il quale restituirà true in caso affermati-vo, false in caso negativo:

if ([[NSFileManager defaultManager] fileExistsAtPath:

archivePath])

{

//il file esiste

}

else

{

//il file non esiste

}

Queste informazioni verranno utilizzate permemorizzare la data in cui la sveglia dovrà esse-re avviata in modo da recuperare e utilizzare taleinformazione ad ogni avvio.

AVAUDIOPLAYERAVFramework, (Audio Video FoundationFramework), è un framework che consente di ese-guire e anche registrare brani audioAVAudioPlayer è una delle classi fornite da talelibreria che consente l’esecuzione di un singolofile audio (uno per istanza); AVAudioPlayer con-sente di mettere in pausa e interrompere unaudio, avere informazioni sulla sua durata e sullaposizione in cui è al momento l’esecuzione, con-sente infine di monitorare i vari livelli di volumeassunti dall’audio. Questa classe accetta tutti iformati supportati dall’iPhone:

• AAC• HE-AAC• AMR (Adaptive Multi-Rate, a format for spee-

ch)• ALAC (Apple Lossless)• iLBC (internet Low Bitrate Codec, another for-

mat for speech)• IMA4 (IMA/ADPCM)• linear PCM (uncompressed)

• µ-law and a-law• MP3 (MPEG-1 audio layer 3

Il formato suggerito dalla documentazione è 16-bit, little-endian, linear PCM di tipo CAF. È possi-bile convertire i proprio file audio in questo for-mato utilizzando il tool chiamato afconvertaccessibile tramite la finestra di terminale di MacOS:

/usr/bin/afconvert -f caff -d LEI16 {INPUT}{OUTPUT}

Nel caso di esecuzione multipla viene consigliatol’utilizzo del formato IMA/ADPCM (IMA4), mentreper l’ascolto di file singolarmente suggerisceMP3, ALAC (Apple Lossless), AAC, IMA4. Il primofile che viene eseguito accede direttamente allerisorse hardware, mentre i successivi sarannoeseguiti via software. Apple raccomanda nelladocumentazione di utilizzare questa classe pereseguire qualunque tipo di effetto audio, a menodi avere necessità di gestire in modo distinto icanali stereo, di avere una sincronizzazione pre-cisa, o quando si utilizzano file provenienti daflussi esterni, come avviene ad esempio per leweb radio. Apple fornisce numerosi frameworkoltre ad AVFramework:

• Media Player framework: per eseguire branimusicali, audio book, podcasts;

• Audio Toolbox framework: per eseguireaudio con precise necessità di sincronizzazio-ne, o analisi o conversione, incluso maggiorecontrollo sulle fasi di registrazione;

• Audio Unit framework: per utilizzare pluginaudio;

• OpenAL framework: viene consigliato comela migliore soluzione per eseguire e gestiremusiche per i videogiochi, e utilizza OpenAL1.1.

Tornando ora a AVAudioPlayer, nel nostro proget-to utilizzeremo un loop audio, il tipico scandiredel tempo di un orologio a tempo, e un suonoche avviserà dell’allarme. Prima di effettuarequalunque operazione è necessario aggiungereAVFoundation.framework tra i framework che uti-lizzerà il progetto e importarlo all’interno diMainViewController (#import <AVFoundation/AVFounda tion.h>). Il codice per eseguire un fileaudio è relativamente breve: prima provvediamoa identificare il path completo della risorsa che ciinteressa, (un file mp3 in questo caso), poi cree-remo un’istanza di AVAudioPlayer, imposteremo ilvolume, il numero di esecuzioni e lo avvieremo:

NSString *path = [[NSBundle mainBundle]

pathForResource:@"clock" ofType:@"mp3"];

NOTA

ULTERIORIAPPROFONDIMENTI

Consultare ladocumentazione onlinedenominata Exception

Programming Topics forCocoa per la gestione delle

eccezioni e per l’elencodelle eccezioni di default.

040-045:088-093-corsi-xsl 29-12-2009 16:53 Pagina 42

Page 56: 86746175 iPhone Programming

iPhone programming 56

iPhone programming Timer e suoni: realizziamo un progetto completo

ht tp ://www. ioprogrammo. i t Febbraio 2010/ 43 G

Timer e suoni: realizziamo un progetto completo � MOBILE

AVAudioPlayer player = [[AVAudioPlayer alloc]

initWithContentsOfURL:[NSURL fileURLWithPath:path]

error:nil];

player.volume = 0.4f;

[player prepareToPlay];

[player setNumberOfLoops:-1];

[player play];

AVAudioPlayer ha un costruttore che accetta unURL, (che in questo caso sarà la posizione assun-ta nel nostro software dalla risorsa chiamataclock.mp3), e una variabile per memorizzare pos-sibili errori (in questo progetto la abbiamo igno-rata, impostandola a nil). Il campo volume è unfloat il cui intervallo è [0,1], dove con 0 si intendeil silenzio, mentre con 1 il massimo valore con-sentito; il metoto prepareToPlay memorizzal’audio in un buffer prima di avviare l’esecuzionein modo da evitare interruzioni dovute al cachingdel file durante il play; setNumberOfLoops: puòassumere un qualunque valore negativo per indi-care un loop infinito, 0 per una singola esecuzio-ne, i per (i+1) ripetizioni, inserendo 1 si avrannoquindi due avvii successivi del suono. Se volessi-mo monitorare quando un suono è terminatobasterà aggiungere il metodo audioPlayerDidFinishPlaying: all’interno della nostra classe,appartenente al protocollo AVAudioPlayerDelegate:

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer

*)player successfully:(BOOL)flag {

//il suono è terminato

}

GESTITE SITUAZIONI“ECCEZIONALI”Chi utilizza linguaggi di alto livello, come JAVA o.NET, ha utilizzato generalmente in maniera

esaustiva quei costrutti che consentono di cattu-rare uno o più errori generati da una non corret-ta esecuzione del proprio applicativo. Un’eccezione è il risultato di un comportamentoanomalo, software o hardware, che il program-matore dovrebbe gestire per evitare che il pro-prio applicativo vada in crash e venga terminato.Per sapere se un metodo o una classe generanouna o più eccezioni è necessario consultare ladocumentazione in linea. La sintassi necessariaper catturare queste eccezioni è estremamentesemplice, basta inserire il codice che si vuolemonitorare all’interno di un blocco di parentesigraffe a cui si antepone @try; con @catch si deli-mita quella parte di codice che dovrà gestire inmodo opportuno l’arrivo dell’eccezione, adesempio deallocando una risorsa, o cercando dirisolvere il problema; @finally invece è un bloccoche viene sempre eseguito e che generalmenteviene adoperato per effettuare comuni operazio-ni di release e pulizia delle risorse utilizzate nelblocco @try. Poiché il codice racchiuso da @final-ly viene sempre eseguito risulta estremamenteutile perché fornisce un unico punto in cui effet-tuare le tipiche operazioni di gestione delle risor-se, invece di doverle ripetere al termine di @try edi @catch. Esistono numerosi tipi di eccezioni,tutte istanze di NSException, che si distinguonoper il nome (il valore del campo name, di tipoNSString) che queste assumono. Oltre a quellepredefinite, ne esistono anche altre presenti inalcuni precisi contesti, e che sono comunquedescritte approfonditamente nella documenta-zione; incapperete all’inizio molto spesso inNSRangeException, quando accederete a indiciinesistenti di strutture dati (problema che non sipresenta analizzandole utilizzando Enumeratorse Fast Enumerators in ambienti Thread Safe), eNSInvalidArgumentException, quando passereteparametri non validi ad un metodo.

@try {

//codice da monitorare

}

@catch (tipoeccezione *eccezione) {

//nel caso avvenga un’eccezione di tipo

tipoeccezione viene gestita

}

@finally {

//questo blocco di codice viene sempre eseguito

}

Nel caso di più eccezioni si potranno inserirediversi blocchi @catch, in questo caso vieneprima analizzato se l’eccezione lanciata dentro@try è una versione realizzata ad hoc dal pro-grammatore, di nome CustomException e in casonegativo viene confrontata con NSException:

RIFERIMENTI WEBCreazione dell'account, perscaricare l'SDK econsultare ladocumentazione:http://developer.apple.com/iphone/

NOTA

Fig. 1: La procedura necessaria per importare il fra-mework

040-045:088-093-corsi-xsl 29-12-2009 16:53 Pagina 43

Page 57: 86746175 iPhone Programming

57 iPhone programming

iPhone programmingTimer e suoni: realizziamo un progetto completoMOBILE � Timer e suoni: realizziamo un progetto completo

ht tp ://www. ioprogrammo. i t

G 44 /Febbraio 2010

@try {

}

@catch (CustomException *ce) {

}

@catch (NSException *ne) {

}

@finally {

}

Se si volesse gestire in un unico blocco @catchtutte le possibili eccezioni è sufficiente cattu-rare quelle appartenenti alla classeNSException che, essendo la più generica dacui derivano tutte le altre, viene sempre riscon-trata. Questa pratica è molto comune, maspesso è più opportuno differenziare i tipi dieccezioni per avere un controllo più specificodi queste situazioni. Realizziamo un sempliceblocco di codice in cui teniamo sotto controlloun metodo fornito dalla nostra classe cheaccetta come parametro un NSMutableArrayche non ha alcun elemento (è infatti stato ini-zializzato con capacità nulla);

NSMutableArray *anArray = nil;

array = [[NSMutableArray alloc]

initWithCapacity:0];

@try {

[self metodo:anArray];

}

@catch (NSException *exception) {

}

@finally {

[anArray release];

}

Nel caso in cui il metodo che abbiamo invoca-to lanci un qualunque tipo di eccezione siamoin grado di catturarlo e gestirlo come più rite-niamo opportuno. Poiché quando si presentaun’eccezione i vari blocchi @catch vengonoanalizzati in sequenza, si procede generalmen-te inserendo prima quelle eccezioni apparte-nenti a classi più specifiche, per poi arrivarealle più generiche, dove NSException rappre-senta la più generica (è anche possibile utiliz-zare id come tipo di eccezione più generale manon avrete probabilmente mai questa neces-sità): quando verrà rilevata un’eccezione nelblocco @try sarà cura dell’ambiente di esecu-zione verificare se il primo @catch è adatto,oppure se dovrà procedere con il prossimo. Incaso non ne venga trovato almeno uno, taleeccezione verrà inviata all’istanza che contienequella dove è avvenuto tale evento e, in casoneppure questa sia in grado di gestirlo, conti-nuerà il suo tragitto fino ad un certo punto,

identificabile con il contenitore principaleUIApplicationMain utilizzato all’interno dimain.m, dopo di che si verificherà un crash delsoftware. Quando viene segnalato nella docu-mentazione in maniera esplicita chel’invocazione di un metodo può lanciareun’eccezione, è sempre consigliato provvederea gestire tale evenienza. Nel caso non si deside-ri gestire un’eccezione è possibile rilanciarla,girarla all’oggetto che contiene l’istanza in cuici troviamo, utilizzando @throw:

@try {

//codice che lancia un’eccezione

}

@catch(NSException *e) {

@throw; // rilancia l’eccezione

}

Anche in questo caso verrà utilizzata la procedu-ra di ricerca progressiva di @catch in grado digestirla. L’utilizzo delle eccezioni dovrebbe essere ristret-to solo a questo preciso scopo: gestire comporta-menti anomali che richiedono un preciso inter-vento da parte dello sviluppatore, ma è anchepossibile utilizzarle per rappresentare situazioninon anomale, come la pressione di alcunesequenze di tasti, che darebbero quindi inizio aduna precisa sequenza di eventi: tale comporta-mento, sebbene possibile, viene sconsigliato daApple. NSException è corredato di un campo chiamatouserInfo, un NSDictionary nel quale è possibileinserire qualunque tipo di informazione alloscopo di passare alcuni dati al @catch che prov-vederà ad analizzarlo; tale utilizzo verrà spiegatoin un altro articolo dove mostreremo come rea-lizzare eccezioni customizzate. Per concludere,può risultare utile mostrare a schermo (in genereviene utilizzato un UIAlertView), o in un log, comenel prossimo esempio mostrato, la causa dell’er-rore e il tipo di eccezione, NSException, e conse-guentemente tutte le classi derivate, fornisce duecampi stringa proprio per questo scopo, reason ename:

@catch(NSException *e) {

//gestione eccezione

NSLog (@”EXCEPTION:%@

(%@)”,e.reason,e.name);

}

LA CLASSE NSARCHIVERLa classe NSArchiver fornita da Cocoa è ingrado di convertire in byte e da byte (in nume-

NOTA

ECCEZIONECon il termine “eccezione”

si intende uncomportamento anomalo

del proprio applicativo,generato da cause

software o hardware; èsempre consigliato gestirele eccezioni per evitare un

crash.

040-045:088-093-corsi-xsl 29-12-2009 16:53 Pagina 44

Page 58: 86746175 iPhone Programming

iPhone programming 58

iPhone programming Timer e suoni: realizziamo un progetto completo

ht tp ://www. ioprogrammo. i t

rosi linguaggi viene adoperato il termine seri-alizzare e deserializzare), istanze di oggetti,scalari, strutture, stringhe e array, ma non con-sente di effettuare questa operazione perunion, void *, puntatori a funzione e sequenzedi puntatori. Per quanto riguarda gli NSArray egli NSDictionary questi devono contenere solole strutture dati supportate per essere serializ-zate/deserializzare. La documentazione è suf-ficientemente esplicita a riguardo:Only instances of NSArray, NSDictionary,NSString, NSDate, NSNumber, and NSData(and some of their subclasses) can be serialized.The contents of array and dictionary objectsmust also contain only objects of these fewclasses.Per chi non fosse pratico con tali concetti, que-ste operazioni permettono di memorizzare lostato di un’istanza su un determinato file.Adoperando poi la deserializzazione è possibi-le recuperare questo stato e riutilizzarlo perripristinare l’istanza nella stessa configurazio-ne precedente. Utilizziamo questa classe permemorizzare all’interno della memoria deltelefono la data impostata dall’utente per lasveglia; perché questa procedura funzioni cor-rettamente è necessario che la classe da cuideriva l’istanza su cui utilizziamo NSArchiversia conforme al protocollo chiamato NSCodingil quale richiede che vengano implementati iseguenti metodi:

• (void)encodeWithCoder:(NSCoder *)encoder (for-male)

• (id)initWithCoder:(NSCoder *)decoder

Solo encodeWithCoder viene esplicitamenterichiesto, mentre initWithCoder, che provvedealla deserializzazione, è opzionale. Nonmostreremo come realizzare il corpo di questimetodi perché la classe che dovremo memo-rizzare nel telefono, NSDate, è già conforme aquesto protocollo. Per capire se una classe sup-porta out of the box questa tecnica basterà con-sultare la documentazione per verificare seNSCoding appare tra i protocolli supportati.Questa procedura può essere utilizzata permemorizzare informazioni arbitrarie in manie-ra estremamente semplice, in un videogioco oin un applicativo, esentando dall’obbligo dicreare delle proprie strutture dati, come filexml o di testo, per organizzare tali dati. Permemorizzare la data che l’utente selezioneràadoperando l’UIDatePicker basterà utilizzare ilmetodo della classe NSKEyedArchiver archiveRootObject: toFile: che consente di effettuare laserializzazione immediatamente. Questometodo restituisce true in caso di salvataggio

completato, false altrimenti.

NSString *archivePath = [[self

applicationDocumentsDirectory]

stringByAppendingPathComponent:@"sveglia.cfg"];

NSString storeResult;

if ([NSKeyedArchiver archiveRootObject:alarmDate

toFile:archivePath])

{

storeResult = @"Configurazione salvata con

successo.";

}

else

{

storeResult = @"Impossibile salvare il file!";

}

Come si può vedere, salvare lo stato diun’istanza all’interno di un file locale èun’operazione estremamente semplice; riotte-nere invece la data all’avvio dell’applicativocomporta l’utilizzo di @try/@catch poichè,come viene esplicitato dalla documentazione:This method raises an NSInvalidArgumentException if the file at path does not contain avalid archive. È possibile evitare di utilizzarequesta pratica verificando prima l’esistenzadel file utilizzando il metodo fileExistsAtPathfornito dalla classe NSFile Manager, mostratoin un precedente paragrafo.

@try

{

if ((alarmDate = [NSKeyedUnarchiver

unarchiveObjectWithFile:archivePath])){

[alarmDate retain];

....

}

@catch (NSException * e)

{

//Ignoriamo l'eccezione di tipo

NSInvalidArgumentException che viene lanciata nel

caso sveglia.cfg non venga trovato

}

CONCLUSIONIIn questo articolo abbiamo introdotto nume-rosi argomenti interessanti che consentono direalizzare soluzioni anche molto complesse,giochi compresi. Il progetto annesso alla rivistaè completo e mostra come si integrano tuttiquesti concetti. Nei prossimi articoli continue-remo questo viaggio nell’universo iPhone eObjective-C. Buona programmazione.

Andrea Leganza

Febbraio 2010/ 45 G

Timer e suoni: realizziamo un progetto completo � MOBILE

L’AUTORE

Andrea LeganzaLaureato in IngegneriaInformatica, da oltre undecennio realizza soluzionimultimediali, e non, supiattaforme e con linguaggidiversi. Certificato AdobeACE - Adobe Flex 3 and AIRCertified Expert, e EUCIPCore, appassionato difotografia, linguagiapponese e istruttore dinuoto FIN, è attualmenteimpegnato in numerosiprogetti multimediali,anche con iPhone, conalcune società nazionali edinternazionali; ècontattabile [email protected] odirettamente sul sitowww.leganza.it.

040-045:088-093-corsi-xsl 29-12-2009 16:53 Pagina 45

Page 59: 86746175 iPhone Programming

59 iPhone programming

iPhone programmingAccedere ai dati dell’accelerometro dell’iPhone

UN ACCELEROMETROPER AMICOL’ACCELEROMETRO PRESENTE NELL’IPHONE È UNO DEI COMPONENTI PIÙ EFFICACI NELCONSENTIRE ALL’UTENTE UN’INTERAZIONE CON GIOCHI E APPLICATIVI MAGGIORMENTESEMPLICE E INTUITIVA. VEDIAMO COME INTEGRARLO NELLE NOSTRE APPLICAZIONI

In questo articolo andremo a realizzare un pro-getto minimale che ci consentirà comunque diapprofondire un argomento molto interessante:

ricevere, analizzare, e conseguentemente utilizzare idati provenienti da uno dei sensori più utili presentisia nell’iPhone che nell’iPod Touch: l’accelerometro.Questo piccolo componente si trova in un numerosempre crescente di dispositivi, merito anche delsuccesso ottenuto da quando è stato inserito nelcontroller della Wii, il wiimote. Nell’iPhone questocomponente viene principalmente adoperato perautomatizzare la rotazione dell’interfaccia graficadi 90° o 180°. Con il rilascio della versione 3 delfirmware (e del relativo SDK) è stata introdotta lafunzionalità di shake, di scuotimento, che vieneutilizzata per annullare l’operazione corrente,oppure per ripetere quella precedente, ma che sipresta a centinaia di diverse interpretazioni,dipendenti solo dalla fantasia dello sviluppatore.Questo componente è uno di quelli che necessitadi un dispositivo reale (non basta il simulatoresoftware) per essere testato: simulare i suoi dati,utilizzando valori casuali o con una certa pianifi-cazione, non sarà mai in grado di fornire un feed-back accurato che consenta di constatarel’effettiva efficacia di come si interpretano talidati. L’accelerometro è un componente elettroni-co realizzato dalla STMicroelectronics, ed è unmodello a tre assi, simile nel funzionamento aquello installato nel controller della Wii, ilWiimote, ma in grado di rilevare teoricamenteaccelerazioni di maggiore intensità (fino a 8g); ilsensore è in grado di funzionare in due modalità+/-2g e +/-8g, nel secondo caso si ha una perditadi precisione di circa quattro volte rispetto allemisurazioni ottenibili quando in modalità +/-2g, eper tale motivo Apple ha deciso di limitare il rangedi valori misurabili dal dispositivo per consentiredi ottenere una maggiore accuratezza delle misu-razioni. È quindi possibile ottenere solo valoricompresi tra -2g e +2g, con una precisione di circa0,018g, e purtroppo non è permesso modificareattraverso l’API quale modalità il telefono deveutilizzare (2g o 8g).

INTERFACCIA GRAFICA:LA ROTAZIONEQuando abbiamo trattato di UIViewController eUITable non abbiamo volutamente accennato aquesta possibilità di ruotare l’interfaccia grafica inbase all’effettiva posizione del telefono perché si èdeciso di destinare tali informazioni al presente arti-colo. Queste rotazioni vengono gestite semplice-mente consultando una variabile, senza interrogaredirettamente l’accelerometro. La visualizzazione“landscape”, orizzontale, è estremamente utile innumerosi contesti, non fornire ad un utente talepossibilità può avere un impatto negativo sull’opi-nione degli utilizzatori del vostro software e quindi,nel caso decidiate di vendere il vostro applicativo,potrebbe portare ad una riduzione delle venditedovute a feedback non positivi. È estremamentesemplice abilitare la propria interfaccia in modo chesvolga tale operazione in maniera automatica,basterà decommentare un metodo, chiamatoshouldAutorotateToInterface Orientation, creatoautomaticamente quando si realizza unUIViewcontroller, o una classe derivata, adoperandoil wizard, e configurare a quali tipi di rotazioni talecontroller dovrà reagire:

// Override to allow orientations other than the

default portrait orientation.

- (BOOL)shouldAutorotateToInterfaceOrientation:

(UIInterfaceOrientation)interfaceOrientation {

// Return YES for supported orientations

return (interfaceOrientation ==

UIInterfaceOrientationPortrait);

}

Dopo aver decommentato il metodo analizziamosia il parametro che riceve che il suo codice interno:interfaceOrientation è di tipo UIInterfaceOrientation, una enum in linguaggio C, i cui possibi-li valori sono i seguenti:

typedef enum {

UIDeviceOrientationUnknown,

UIDeviceOrientationPortrait, // Device

MOBILE � Accedere ai dati dell’accelerometro dell’iPhone

ht tp ://www. ioprogrammo. i t

G 60 / Marzo 2010

❑ CD ❑ WEBaccelerometro.rar

cdrom.ioprogrammo.it

Conoscenze richiesteOOP - Objectivive-C

SoftwareMacOS X 10.5.4 osuperiore, XCode

Impegno

¥Tempo di realizzazione

REQUISITI

060-063:088-093-corsi-xsl 3-02-2010 12:55 Pagina 60

Page 60: 86746175 iPhone Programming

iPhone programming 60

iPhone programming Accedere ai dati dell’accelerometro dell’iPhone

ht tp ://www. ioprogrammo. i t Marzo 2010/ 61 G

Accedere ai dati dell’accelerometro dell’iPhone � MOBILE

oriented vertically, home button on the bottom

UIDeviceOrientationPortraitUpsideDown, // Device

oriented vertically, home button on the top

UIDeviceOrientationLandscapeLeft, // Device

oriented horizontally, home button on the right

UIDeviceOrientationLandscapeRight, // Device

oriented horizontally, home button on the left

UIDeviceOrientationFaceUp, // Device

oriented flat, face up

UIDeviceOrientationFaceDown // Device

oriented flat, face down

} UIDeviceOrientation;

Quando otteniamo un orientamento di tipo portraitsignifica che il telefono è tenuto in posizione verti-cale, sia quando il pulsante home del telefono puntaverso il basso, che quando il telefono è capovolto etale pulsante punta verso l’alto (UpsideDown); land-scape ovviamente indica che il telefono è tenutoorizzontalmente, con il pulsante home posizionatosul lato sinistro (Landscape Right), o sul lato destro

(Landscape Left); FaceUp e Down vengono utilizzatiper notificare che il telefono ha lo schermo versoposizionato in basso, verso il pavimento, o versol’alto. Queste modalità consentono di far reagire lapropria interfaccia in base a 6 disposizioni predefi-nite del telefono, senza necessariamente analizzare idati che provengono dal telefono e questa modalitàdi consultazione è la più indicata per modificare lagrafica del proprio software. Il metodo viene interro-gato dal sistema operativo del telefono che forniràtale parametro e attenderà un valore booleano chegli consentirà di decidere se ruotare o menol’interfaccia automaticamente. La scelta se ruotare omeno dipende da un semplice confronto tra il valo-re passato come parametro al metodo e i possibilistati del telefono. Decommentando questo metodonon si imposta ancora alcuna rotazione: per talemotivo, se volessimo far ruotare la nostra interfacciagrafica di 90 o 180 gradi, dovremo aggiungere, oltre aUIInterfaceOrientationPortrait, anche le altre moda-lità che vogliamo facciano scattare il meccanismo dirotazione. Ciò si effettua separando le modalità sup-portare utilizzato OR (simbolo ||):

- (BOOL)shouldAutorotateToInterfaceOrientation:

(UIInterfaceOrientation)interfaceOrientation

{

// Return YES for supported orientations

return (interfaceOrientation ==

UIInterfaceOrientationPortrait ||

UIInterfaceOrientationLandscapeLeft ||

UIInterfaceOrientationLandscapeRight);

}

In questo modo abbiamo automatizzato la rotazio-ne sia quando il software è verticale e quando èorizzontale in entrambi i versi. Questo tipo di gestione dell’accelerometro è l’unicache è possibile testare parzialmente anche con ilsolo simulatore: basterà, tenendo premuto il tastomela, operare sui i tasti cursore per far ruotare di90° a sinistra e a destra l’interfaccia, e verificareconseguentemente come reagisce il software.Purtroppo le modalità portraitupsidedown e face-up/down non sono disponibili. La rotazione auto-matica si propaga anche ai componenti contenutiall’interno del ViewController, UIView, immagini etutti gli altri inseriti dallo sviluppatore, ma non hasempre l’esito sperato, ciò è dovuto al fatto che inalcune situazioni è necessario adoperare altri stru-menti di modifica delle coordinate, che risultanopiù adatti (le cosiddette Core AnimationTransforma tions, di cui parleremo in un altro arti-colo). Prendiamo come esempio un’immagineposizionata nel centro della nostra interfaccia.Quando andremo a ruotare l’interfaccia otterremoun effetto simile a quello visibile in Fig.5.La palla da rugby si è posizionata in maniera noncorretta, un risultato alquanto indesiderato, vedre-mo in un prossimo articolo come effettuare delletrasformazioni sulle coordinate (le cosiddette tra-sformazioni affini) degli oggetti presenti nell’inter-faccia che permetteranno di ottenere il risultatodesiderato.

I MOTION EVENTSL’SDK 3.0 ha introdotto il concetto di motionevents, eventi generati dal movimento. Questi sonoscatenati dopo alcune situazioni identificate dal-l’accelerometro, e non richiedono quindi misura-zioni continue da parte dell’utente. Gli eventidisponibili sono in numero limitato:

• motionBegan• motionEnded• motionCancelled

Tutti i tre eventi ricevono due parametri: il primo,di tipo UIEventSubtype, consente di identificare seil movimento è stato identificato come apparte-nente ad una certa tipologia. Al momento l’unico

Fig. 1: La disposizionedegli assi e dei versi posi-tivi e negativi dell’accele-rometro

Fig. 2: La comune visua-lizzazione verticale di unatabella

Fig. 3: La rotazione viene effettuata automaticamente

060-063:088-093-corsi-xsl 3-02-2010 12:55 Pagina 61

Page 61: 86746175 iPhone Programming

61 iPhone programming

iPhone programmingAccedere ai dati dell’accelerometro dell’iPhoneMOBILE � Accedere ai dati dell’accelerometro dell’iPhone

ht tp ://www. ioprogrammo. i tG 62 / Marzo 2010

effettivamente utile è UIEventSubtype MotionShake(UIEventSubtypeNone non fornisce alcuna infor-mazione utile) il quale consente di identificare sel’utente ha agitato il telefono: se si volesse suppor-tare la possibilità di annullare un’operazione discrittura di una email ad esempio agitando il dispo-sitivo, basterebbe verificare se la variabile motionassume valore pari a questo tipo di movimento conun semplice if e in caso affermativo agire di conse-guenza (cancellando il testo, magari richiedendocon un UIAlert la conferma di tale operazione). È possibile emulare lo shake tramite il simulatoreadoperando la voce omonima presente nel menuhardware.

- (void)motionBegan:(UIEventSubtype)motion

withEvent:(UIEvent *)event

{ //gestione evento}

- (void)motionEnded:(UIEventSubtype)motion

withEvent:(UIEvent *)event

{//gestione evento }

- (void)motionCancelled:(UIEventSubtype)motion

withEvent:(UIEvent *)event

{ //gestione evento

if (event.type == UIEventSubtypeMotionShake) {

//se il movimento è di shake agiamo di conseguenza

}

}

Il parametro event non ha alcuna utilità in questocontesto, viene utilizzato per altri tipi di eventi,quelli touch (il tocco dello schermo) e fornisceinformazioni sulle coordinate dei dei vari punti incui è avvenuta l’interazione dell’utente. L’utilizzo dimotionBegan e motionEnded è immediato, mentrel’utilità di motionCancelled è meno intuitiva: que-sto metodo assume importanza quando un eventoidentificato come uno di tipo shake dura troppotempo (oltre un secondo) oppure quando il fra-mework Cocoa Touch richiede l’interruzione dell’a-nalisi del movimento, in entrambi i casi questoblocco di codice dovrebbe contenere il codice per

ripristinare lo stato del software prima delle modi-fiche effettuate all’interno di motionBegan. Nel caso in cui il movimento non venga gestito innessun UIViewController, e la proprietà applica-tion SupportsShakeToEdit del nostro applicativo siaimpostata a YES (accessibile tramite il singletonUIApplication di cui parleremo in un prossimoarticolo) verrà presentato un menu di scelta traundo (annullamento) o redo (ripetizione). Questitre metodi sono meno utili rispetto alla consulta-zione dei dati forniti dall’accelerometro ma nelcaso dell’identificazione del movimento di shakeforniscono il modo più semplice e veloce per sup-portare questo tipo di interazione.

IL PRELIEVO DELLEINFORMAZIONI Ora che abbiamo mostrato due situazioni in cui fareuso in maniera implicita delle informazioni prove-nienti dall’accelerometro, realizziamo il codicenecessario per abilitare e leggere i suoi dati in modoesplicito. Ogni applicativo può accedere a una solaistanza della variabile che fornisce i dati dell’accele-rometro, un singleton secondo la terminologiaobject oriented, tale istanza appartiene al tipo didato UIAccelerometer; per abilitare la ricezione deidati si provvede a impostare se stessi, la classe in cuisi analizzano i dati, come delegate dell’accelerome-tro, mentre per disabilitare la ricezione è sufficientereimpostare a nil tale delegate.

int frequency = 50;

UIAccelerometer* myAccelerometer;

-(IBAction)configurazioneAccelerometro

{

myAccelerometer = [UIAccelerometer

sharedAccelerometer];

myAccelerometer.updateInterval = 1 / frequency;

//Impostiamo il delegate

myAccelerometer.delegate = self;

//Da questo momento verranno inviati i dati

dall’accelerometro tramite l’invocazione del metodo

accelerometer: didAccelerate

}

Impostando la classe in cui si trova come delegate sidichiara di rispettare il protocollo chiamatoUIAccelerometerDelegate, come suggerito in articoliprecedenti è sempre consigliato dichiarare tale ope-razione nel file di interfaccia .h:

@interface accelerometroViewController :

UIViewController <UIAccelerometerDelegate>

La variabile myAccelerometer viene creata richie-

NOTA

SPECIFICHEHARDWARE

A questi indirizzi è possibileconsultare la

documentazione dei duemodelli di sensori utilizzati

nelle diverse versionidell’iPhone e dell’iPod

Touch:LIS302DL:

http://www.st.com/stonline/products/literature/ds/12

726/lis302dl.pdfLIS331DL:

http://www.st.com/stonline/products/literature/ds/13

951.pdf

Fig. 5: La rotazione automatica si è rivelata scarsamenteefficace, generando un risultato indesiderato nella mag-gior parte dei casi

Fig. 4: Il pallone da rugbyè al centro dell’interfac-cia

060-063:088-093-corsi-xsl 4-02-2010 17:17 Pagina 62

Page 62: 86746175 iPhone Programming

iPhone programming 62

iPhone programming Accedere ai dati dell’accelerometro dell’iPhone

ht tp ://www. ioprogrammo. i t Marzo 2010/ 63 G

Accedere ai dati dell’accelerometro dell’iPhone � MOBILE

dendo al singleton UIAccelerometer la sua istanza,tramite il metodo sharedAcelerometer. È possibilepoi prelevare dati a diverse frequenze, in questo casoabbiamo scelto 50Hertz, ciò significa il prelievo diuna misurazione del sensore ogni 20 millisecondi.Per chi non avesse conoscenza di queste definizioni1Herz indica una misurazione ogni secondo, 2 Herz2 al secondo, 10 Hertz 10 al secondo e così via. Il range di valori consentiti e consigliati da Applesono:

• 10-20 Herz: per misurare il verso del telefono• 30-60 Herz: per giochi e applicativi in cui è richie-

sta maggiore precisione• 70-100 Herz: per misurazione con variazioni di

posizione molto elevata

I valori verso i 100 Herz non vengono quasi mai uti-lizzati perché raramente è necessaria una tale preci-sione. L’asse z assumerà un valore prossimo a +1quando il telefono sarà appoggiato su una superficiepiana, parallela al pavimento, -1 quando lo schermotoccherà il piano; y assumerà valore -1 quando iltelefono sarà in posizione portrait con il pulsantehome in basso, +1 quando il pulsante home saràrivolto verso l’alto; x varrà -1 quando lo schermosarà rivolto verso sinistra e +1 verso destra. Tutti ivalori compresi tra + e -1 per tutti gli assi verrannoassunti quando non si posiziona il telefono perfetta-mente perpendicolare al piano. La somma delle tremisurazioni deve valere in valore assoluto circa 1.Dopo aver configurato la modalità di ricezione deidati, notifichiamo alla variabile che siamo in gradodi ricevere e gestire le sue informazioni, per talemotivo la informiamo che siamo un delegate; a que-sto punto è necessario implementare il metodo chel’accelerometro invocherà ad ogni aggiornamento:

- (void)accelerometer:(UIAccelerometer

*)accelerometer didAccelerate:(UIAcceleration

*)acceleration

{

...

Avevamo detto che i dati provenienti dall’accelero-metro erano di tipo double, ma in questo casoabbiamo adoperato UIAccelerationValue, ebbenequesto tipo di dato è intercambiabile con double,essendo un suo alias:

typedef double UIAccelerationValue;

Per finire, creiamo un metodo che verrà invocatoquando si desidera che termini il rilevamento:

-(void) terminaAnalisi {

myAccelerometer.delegate = nil; }

Nel caso si desideri gestire all’interno di un soloUIViewController queste misurazioni è obbligato-rio fare in modo che il delegate sia impostato a nilprima che il viewcontroller venga rimosso dallostack di controller. Se ci troviamo in un sistema dinavigazione tra controller, poichè altrimenti sirischia il verificarsi di crash casuali dovuti all’invo-cazione del metodo accelerometer: didAcceleratesu un’istanza non più esistente (con il verificarsidella chiamata ad uno zombie quindi). Per visua-lizzare immediatamente i dati provenienti dallamisurazioni dei tre assi basterà creare tre UILabel,impostarle come IBOutlet, e modificare il testocontenuto in base alle rilevazioni:

- (void)accelerometer:(UIAccelerometer

*)accelerometer didAccelerate:(UIAcceleration

*)acceleration

{

UIAccelerationValue x, y, z;

x = acceleration.x;

y = acceleration.y;

z = acceleration.z;

//Aggiornamento del testo dei tre UILabel

xLabel.text = [NSString

stringWithFormat:@"%g",x];

yLabel.text = [NSString

stringWithFormat:@"%g",y];

zLabel.text = [NSString

stringWithFormat:@"%g",z];

}

UNA WII COME TESTERAnche se all’inizio dell’articolo abbiamo dichia-rato che non è possibile simulare senza il dispo-sitivo reale i dati dell’accelerometro, in realtàesistono alcune soluzioni che consentono dibypassare questa richiesta, una di queste consi-ste nell’adoperare un controller della Wii, colle-garlo via Bluetooth e fornire tali informazioniattraverso un web server, che verrà consultatoda codice appositamente realizzato nel proprioapplicativo sull’iPhone. Questa soluzione com-porta sia la realizzazione del codice client neltelefono che di quello server al quale fa riferi-mento il dispositivo e non è quindiun’operazione che un utente non pratico con unaltro linguaggio di programmazione (ad esem-pio C#) potrebbe realizzare in maniera autono-ma in tempi relativamente brevi. Inoltre si devetener conto che il sensore del wiimote invia datifino a 3g, mentre, come abbiamo precisato inprecedenza, nell’iPhone si arriva fino a 2g.

Andrea Leganza

RIFERIMENTI WEBCreazione dell'account, perscaricare l'SDK econsultare ladocumentazione:http://developer.apple.com/iphone/

NOTA

L’AUTORE

Andrea LeganzaLaureato in IngegneriaInformatica, da oltre undecennio realizza soluzionimultimediali, e non, supiattaforme e con linguaggidiversi. Certificato AdobeACE - Adobe Flex 3 and AIRCertified Expert, e EUCIPCore, appassionato difotografia, linguagiapponese e istruttore dinuoto FIN, è attualmenteimpegnato in numerosiprogetti multimediali,anche con iPhone, conalcune società nazionali edinternazionali; ècontattabile [email protected] odirettamente sul sitowww.leganza.it.

060-063:088-093-corsi-xsl 3-02-2010 12:55 Pagina 63

Page 63: 86746175 iPhone Programming

63 iPhone programming

iPhone programmingGestione dell’interfaccia: i comandi touch

IPHONE: GESTIRE IL MULTI-TOUCHLO SCHERMO MULTITOUCH CONSENTE DI INTERAGIRE CON UN DISPOSITIVO SENZA LANECESSITÀ DI FORNIRE INGOMBRANTI TASTIERE. IMPARIAMO A INTERCETTARE LE AZIONI DEGLI UTENTI E A GESTIRLE IN MODO DA NON FAR RIMPIANGERE TASTIERA E MOUSE

ht tp ://www. ioprogrammo. i t Aprile 2010/ 55 G

Gestione dell’interfaccia: i comandi touch � MOBILE

L’evoluzione tecnologica e la conseguente discesadei prezzi hanno consentito ai produttori didispositivi mobili di inserire nei propri prodotti

uno o più schermi touch screen: quello che prima era uncomponente riservato a contesti specializzati, è divenu-to sempre più frequente nell’elettronica di largo consu-mo. Nell’interfaccia dell’iPhone il multitouch consentedi interagire in molti modi con i componenti grafici:

● tocco singolo (single tap)● tocchi multipli (multi tap)● trascinamento (drag)● scrolling verticale (swipe verticale)● strisciata (swipe orizzontale)

Il termine Swipe, utilizzato nella documentazioneiPhone, è sinonimo del più comune “scrolling”, ed èquindi quell’operazione adoperata generalmenteper passare da un contenuto all’altro, immagini ovideo ad esempio, e che consiste in un repentinomovimento di uno o più dita da destra a sinistra oviceversa; In questo articolo vedremo di accedereai dati che questo dispositivo è in grado di fornireallo sviluppatore, e li utilizzeremo per i nostri scopi.

COME FUNZIONA UN TOUCH SCREENIl multi-touch presente nell’iPhone è un insieme distrati costituiti da diversi materiali e in alcuni livel-li è percorso da componenti elettrici che, quandovengono a contatto con gli strati superiori, identifi-cano tale situazione come la pressione da partedell’utente di una precisa area dello schermo. Glistrati necessari per il funzionamento del multitou-ch sono interposti tra una copertura antiriflesso,quella che effettivamente l’utente tocca con le pro-prie dita, e il display LCD vero e proprio. Cosa suc-cede quando un utente tocca lo schermo? I varistrati superficiali collidono nei punti in cui avven-gono le pressioni, questi eventi generano deisegnali elettrici abbastanza imprecisi a causa di

normali interferenze elettriche, che rappresentanoaree di schermo di circa 1 cm di lato (tale è ladimensione dell’area che andiamo a toccare effet-tivamente), su queste informazioni vengono effet-tuate delle stime per identificare al meglio la regio-ne dove effettivamente l’utente voleva interagire,infine vengono calcolate le coordinate del centro diqueste aree e vengono inviate, se richiesto, al vostrosoftware, altrimenti vengono ignorate.

UIEVENT E UITOUCHOgni componente può rispondere alle interazionidell’utente, purché discenda dalla classeUIResponder, UIViewController, UIView, e conse-guentemente le loro classi figlie (in pratica tutti icomponenti grafici), derivano da tale padre, e pertale motivo sono pienamente autorizzate a gestiretali eventi. Ovviamente, poiché unUIViewController contiene al suo interno unUIView, potrà intercettare il tocco prima delle viewe gestirlo senza demandare tale operazione ad unadelle sue view.

I seguenti quattro metodi:

- (void)touchesBegan:(NSSet *)touches

withEvent:(UIEvent *)event;

- (void)touchesMoved:(NSSet *)touches

withEvent:(UIEvent *)event;

- (void)touchesEnded:(NSSet *)touches

withEvent:(UIEvent *)event;

- (void)touchesCancelled:(NSSet *)touches

withEvent:(UIEvent *)event

sono quelli, forniti dalla classe UIResponder, che ciconsentono di intercettare i tocchi effettuati dall’u-tente: non è necessario alcuna operazione aggiun-tiva, (come impostare protocolli o delegate per ini-ziare a gestirli). L’ultimo metodo è meno intuitivo,viene invocato dal sistema quando si scatenanoeventi fuori dal controllo dell’utente, come una

❑ CD ❑ WEBiphone149.zip

cdrom.ioprogrammo.it

Conoscenze richiesteOOP - Objectivive-C

SoftwareMacOS X 10.5.4 osuperiore, XCode

Impegno

¥Tempo di realizzazione

REQUISITI

Fig. 1: Una sempliceinterfaccia grafica perrealizzare un sistema didrag and drop

055-059:088-093-corsi-xsl 4-03-2010 14:34 Pagina 55

Page 64: 86746175 iPhone Programming

iPhone programming 64

iPhone programming Gestione dell’interfaccia: i comandi touch

NOTA

RIFERIMENTI WEBCreazione dell'account, per

scaricare l'SDK econsultare la

documentazione:http://developer.apple.co

m/iphone/

MOBILE � Gestione dell’interfaccia: i comandi touch

ht tp ://www. ioprogrammo. i tG 56 / Aprile 2010

possibile chiamata, una rimozione della view sucui si sta analizzando il tocco (ad esempio per unretain che ne invoca il distruttore), o altra notificacome quello di batteria in esaurimento, che annul-lano il completamento delle operazioni di toccoiniziate dall’utente, in questo metodo generalmen-te si ripristina il sistema allo stato interno prece-dente, corrispondente a quello trovato quando èstato invocato per la prima volta il metodotouchesBegan. Il primo parametro di questi metodi,NSSet, è un’istanza di una classe il cui scopo è quel-lo di raccogliere in sè più oggetti in maniera nonordinata. In questo caso al suo interno troviamotutte istanze della classe UITouch, ognuna a rap-presentare le diverse dita che toccano lo schermo. La classe UITouch fornisce informazioni riguardo

la posizione attuale del tocco (un punto di tipoCGPoint e coordinate (x,y) ottenuto con la chiama-ta al metodo (locationInView:), quella precedente(previousLocationInView:), il numero di pressioniripetute (proprietà tap) del dito sullo schermo edeffettuate in breve sequenza (proprietà tapCotunt),la view in cui questo è stato identificato (proprietàview), l’istante temporale in cui tali coordinatesono cambiate (proprietà timestamp, utile permisurare la durata di un movimento, ad esempio),e la fase del tocco (proprietà phase), informazioneche consente di identificare lo stato del tocco:

UITouchPhaseBegan,UITouchPhaseMoved,UITouchPhaseStationary,UITouchPhaseEnded,UITouchPhaseCancelled,

Quando ci troviamo in un progetto in cui non ciinteressa se uno o più dita toccano lo schermo, mavogliamo sapere la posizione di uno qualsiasi deitocchi, basterà invocare il seguente metodo, resodisponibile dalla classe NSSet, all’interno di unodei metodi touches*:

UITouch *touch = [touches anyObject];

il quale restituisce un’istanza in modo pseudo-casuale (non è detto che sia sempre diversa perogni invocazione successiva); ovviamente se esisteun solo tocco anyObject restituirà sempre questo.Nell’articolo precedente avevamo parlato di come

gestire i dati che provenivano dal sensore,l’accelerometro, presente nel dispositivo, e aveva-mo trattato della famiglia di eventi definiti con lastruct UIEventType che raccoglie al suo interno siagli input di movimento (misurati dall’accelerome-tro) che quelli di tocco:

typedef enum {

UIEventTypeTouches,

UIEventTypeMotion,

} UIEventType;

Il secondo parametro di tutti i metodi touches* è ditipo UIEvent e assumerà come tipo ovviamente ilvalore UIEventTypeTouches. L’istanza appartenentealla classe UIEvent rappresenta quindi lo statoattuale delle dita che sono a contatto con lo scher-mo, e consente di identificare univocamente le sin-gole istanze di UITouch e come è variato il lorostato. Per ottenere i tocchi relativi ad una determi-nata view basta invocare all’interno di uno deimetodi touches* su tale UIEvent il metodotouchesForView:Come riesce il dispositivo a identificare in qualeUIView viene effettivamente effettuato il tocco conuna certa coordinata? Questa ricerca avviene sem-plicemente effettuata in maniera iterativa: si partedal contenitore principale, che racchiude in se tuttigli oggetti visuali del nostro applicativo, laUIWindow, e si procede “scendendo” tra gliUIViewController e le rispettive UIView effettuandosu di queste il metodo pointInside:withEvent. Taleprocedura prosegue per ogni UIView che restitui-sce YES a tale chiamata, fino a raggiungere l’ultimaUIView che diventerà la responsabile della gestionee dell’interpretazione del tocco: tale sequenza diview e viewcontrollers prende il nome di “respon-der chain”, catena di risposta. Nel caso l’ultimaview non fosse configurata per gestire il tocco il

NOTA

TOUCH -SCREENPer una descrizione più

approfondita delfunzionamento del touch-

screen dell’iPhone:http://electronics.howstuff

works.com/iphone1.htm

Fig. 3: La responder chain dell’iPhone

Fig. 2: Il contenuto di una istanza di tipo UIEvent

055-059:088-093-corsi-xsl 4-03-2010 14:34 Pagina 56

Page 65: 86746175 iPhone Programming

65 iPhone programming

iPhone programmingGestione dell’interfaccia: i comandi touch

ht tp ://www. ioprogrammo. i t Aprile 2010/ 57 G

Gestione dell’interfaccia: i comandi touch � MOBILE

metodo di ricerca risalirà tra le UIView (e i rispettvicontrollers) che la contengono in cerca di una ingrado di “reagire” a tale operazione; nel caso peg-giore, quello in cui si ritrovasse di nuovonell’UIWindow, tale evento verrà ignorato.

ABILITARE E DISABILITAREL’INTERAZIONENel caso in cui non si desideri che una view ricevauno o più eventi dovuti al tocco da parte dell’uten-te è possibile disabilitare tale interazione in diversimodi attraverso l’utilizzo di proprietà o metodi for-niti dalla classe UIView:

● Configurandola come trasparente (ex:myUIView.alpha=0.0);

● Configurandola come nascosta (ex:myUIView.hidden=YES);

● Impostando la sua proprietà userInteractionEnabled a NO: (ex: myUIView.userInteractionEnabled=NO);

Nel caso si decidesse di disabilitare completamen-te l’interazione per l’intero applicativo, ad esempioquando si effettuano delle animazioni sarebbe suf-ficiente invocare prima di tale situazionebeginIgnoringInteractionEvents e successivamenteendIgnoringInteractionEvents sul singletonUIApplication (è un’istanza di una classe unicadurante tutta la vita dell’applicativo) nel seguentemodo:

[[UIApplication sharedApplication]

beginIgnoringInteractionEvents];

//Eventi che non richiedono interazione con l’utente

[[UIApplication sharedApplication]

endIgnoringInteractionEvents];

Per default i tocchi multipli non vengono gestitiautomaticamente, sono perciò ignorati e vengonopassate all’applicativo solo quelle informazionirelative al primo tocco rilevato; per abilitare il mul-titouch è necessario invocare nel proprio codice,quando necessario, il metodo multipleTouch-Enabled sulla UIView che dovrà gestirli. Se si desi-dera fare in modo che solo una UIView risponda atutti gli eventi si dovrà impostare la sua proprietà aexclusiveTouch a YES.

LE SEQUENZE DI TAPMultitouch non significa solo gestire i tocchi delledita contemporaneamente senza identificare lasequenza con cui questi sono avvenuti, nell’iPhone

OS è presente una logica in grado di monitorare eanalizzare diversi tipi di interazioni, sia in base alnumero che alla direzione del movimento effettua-to dalle dita che in quel preciso momento sono acontatto con lo schermo. La sequenza tipo di un evento di tipo singolo toccoè costituito dai seguenti passi:

1- un dito tocca lo schermo;2- il dito viene mosso mantenendolo a contatto

con lo schermo (opzionale)3- il dito viene rimosso

Se il movimento, (il secondo passo descritto nellalista), è un’operazione opzionale, la prima e laseconda avvengono obbligatoriamente in un inter-vallo di tempo che può essere più o meno breve,nel caso in cui si effettui la tipica operazione di tap,termine che sta ad indicare la pressione singola opiù volte delle dita, siamo in grado in maniera sem-plice di ottenere il numero di questi tocchi senzanecessariamente dover utilizzare una variabile percontabilizzarli. La sequenza tipo di un evento multi tocco è costi-tuito dai seguenti passi:1 - un primo dito tocca lo schermo;2- un secondo dito tocca lo schermo (il numero

delle dita può essere superiore a due);3 - uno o entrambi effettuano un movimento;4 - in successione vengono rimossi entrambi

SINGLE-TAP E MULTI-TAPCome è stato detto precedentemente è possibileconoscere con estrema facilità il numero di tocchieffettuati in un breve intervallo di tempo dall’uten-te, basta consultare la proprietà tapCount, in gene-re ciò viene fatto all’interno del metodotouchesEnded (quando uno o più dita vengonorimosse quindi).

- (void)touchesEnded:(NSSet *)touches

withEvent:(UIEvent *)event {

for (UITouch *touch in touches) {

if (touch.tapCount == 1) {

Fig. 4: I due checkboxposizionati in basso con-sentono di cambiare ilcomportamento predefini-to della view attraversoInterface Builder

Fig. 5: Una tipica situazione di sequenza di eventi

055-059:088-093-corsi-xsl 4-03-2010 14:34 Pagina 57

Page 66: 86746175 iPhone Programming

iPhone programming 66

iPhone programming Gestione dell’interfaccia: i comandi touch

//do something }

else if (touch.tapCount == 2) {

//do something }

else if (touch.tapCount == 3) {

//do something}

//etc tapCount>3

}

}

IDENTIFICHIAMO EGESTIAMO LO “SWIPE” Abbiamo detto che con il termine swipe si identifi-ca lo scorrimento orizzontale/verticale di uno, opiù dita. Per supportarlo all’interno delle proprieUIView (o UIViewController) è necessario effettua-re alcuni calcoli che identifichino se lo spostamen-to delle dita è dovuto ad un movimento volontariooppure ad un semplice tremolio del dispositivo inmano all’utente; per fare ciò si deve misurare in unintervallo di tempo come variano le coordinate deltocco e, nel caso tale delta sia superiore o uguale adun valore predefinito, si può ritenere con una rela-tiva certezza che l’utente stia effettivamente effet-tuando tale operazione. Vista l’inaccuratezza delmovimento delle dita in una delle due direzioni, èopportuno considerare come swipe qualunquemovimento che comporti uno spostamento all’in-terno di un ipotetico rettangolo , il cui centro geo-metrico è dato dalla coordinata del primo tocco.Studiamo il caso di swipe orizzontale.Se il movimento verticale (sia verso l’alto che ilbasso) supererà una costante predefinita, MASSI-MODELTAVERTICALE, il cui valore è espresso inpixel, non identifichiamo tale movimento comeuno swipe. In tale situazione è probabile chel’utente voglia effettuare o uno scrolling in direzio-ne obliqua oppure in orizzontale, ma per qualchemotivo non è riuscito ad effettuarlo in maniera cor-retta. Di conseguenze se il movimento orizzontalenon sarà maggiore di un certo numero di pixels,espresso dalla variabile MINIMODELTAORIZZON-TALE, allora è probabile che il movimento identifi-cato non sia volontario e lo ignoreremo. Variandotali estremi potremo decidere in che modo reagireai diversi movimenti , forzando l’utente ad effettua-re gesti molto precisi oppure consentendogli unamaggiore libertà.

#define MINIMODELTAORIZZONTALE 8 //minimo

spostamento orizzontale in pixel

#define MASSIMODELTAVERTICALE 6 //massimo

spostamento verticale in pixel

Iniziamo memorizzando la posizione del primotocco nella variabile chiamata startTouchPosition:è di tipo CGPoint, una semplice struttura C

struct CGPoint {

CGFloat x;

CGFloat y;

};

typedef struct CGPoint CGPoint;

I valori che CGPoint potrà rappresentare quandotoccheremo lo schermo con un dito potrannoappartenere a qualunque punto di coordinate (x,y),dove x è compreso tra 0 e 320 e y tra 0 e 480 (0,0bordo alto a sinistra del telefono, 320,480 basso adestra), dovute alla risoluzione dell’LCDdell’iPhone (CGPoint può assumere comunquevalori superiori o inferiori a questi intervalli maovviamente rappresentano posizioni esterneall’LCD e non visibili all’utente). Per determinare se il movimento è contenutoall’interno dell’area utile, effettuiamo semplice-mente il calcolo della differenza tra la coordinataprecedente, misurata all’interno del metodotouchesBegan, e quella rilevata successivamenteall’interno di touchedMoved: in valore assoluto,adoperando la funzione C fabsf:

CGPoint startTouchPosition;

- (void)touchesBegan:(NSSet *)touches

withEvent:(UIEvent *)event

{

UITouch *touch = [touches anyObject];

startTouchPosition = [touch locationInView:self];

}

- (void)touchesMoved:(NSSet *)touches

withEvent:(UIEvent *)event {

UITouch *touch = [touches anyObject];

CGPoint currentTouchPosition = [touch

locationInView:self];

if (fabsf(startTouchPosition.x -

currentTouchPosition.x) >=

MINIMODELTAORIZZONTALE &&

fabsf(startTouchPosition.y - currentTouchPosition.y)

<= MASSIMODELTAVERTICALE) {

if (startTouchPosition.x <

currentTouchPosition.x) {

//swipe a destra

}

else {//swipe a sinistra}

}}

- (void)touchesEnded:(NSSet *)touches

withEvent:(UIEvent *)event {

//azzeriamo la posizione iniziale

startTouchPosition = 0.0;}

- (void)touchesCancelled:(NSSet *)touches

withEvent:(UIEvent *)event {

//azzeriamo la posizione iniziale se è avvenuto un

evento che ha annullato l’operazione

startTouchPosition = 0.0;}

Quando verrà alzato il dito, sarà sufficiente azzera-

MOBILE � Gestione dell’interfaccia: i comandi touch

ht tp ://www. ioprogrammo. i t

G 58 /Aprile 2010

Fig. 6: Una tipica situazio-ne di sequenza multipla di eventi

055-059:088-093-corsi-xsl 5-03-2010 19:10 Pagina 58

Page 67: 86746175 iPhone Programming

67 iPhone programming

iPhone programmingGestione dell’interfaccia: i comandi touch

ht tp ://www. ioprogrammo. i t

re la posizione iniziale per essere di nuovo pronti astudiare i tocchi successivi. È possibile quindi,variando i due valori di delta predefiniti, creare areeverticali per supportare lo scrolling verticale, comeavviene ad esempio per le UITableView o leUIScrollView.

GESTIRE IL TRASCINAMENTOL’operazione di drag&drop, si rivela meno compli-cata rispetto a quella di swipe: basterà infatti iden-tificare quale UIView è stata toccata e muoverla diconseguenza, in risposta agli spostamenti del dito.Esistono due metodi principale per gestire taleoperazione:

gestire tale comportamento all’interno delUIViewController, che provvederà a spostare laUIViewgestire tale comportamento all’interno dellaUIView stessa, creando una classe figlia di UIView.

Nel nostro progetto abbiamo deciso di gestirel’interazione all’interno del viewcontroller: il pregiodi questo approccio è dovuto alla possibilità diidentificare possibili sovrapposizioni tra le view eagire di conseguenza (ad esempio realizzando unsemplice algoritmo di analisi delle collisioni comeviene fatto per i giochi) e quindi dovremo inserire ivari metodi touches* nel suo file di implementazio-ne (.m). Creiamo quindi il nostro progetto, di tipoView-Based Application. Creiamo due IBOutlet(ricordate di chiamare su entrambi il metodorelease nel metodo dealloc allo scopo di evitareleaks), di nome red e green in questo esempio,all’interno del file di interfaccia (.h) della classechiamata progettoViewController:

@interface progettoViewController : UIViewController

{

IBOutlet UIView *red;

IBOutlet UIView *green;

}

@end

Apriamo con interface builder il file chiamatonomeprogettoviewcontroller.xib e trasciniamoall’interno della view principale due ulterioriUIView, ridimensioniamole a nostro piacimento, eimpostiamone i colori in modo da poterle distin-guere visivamente. Premiamo il tasto destro delmouse sulla voce Files Owner e, tramite la solitaoperazione di trascinamento dei due IBOutlet, col-leghiamoli con le due UIView appena create.Ora non ci resta che implementare i soliti metodiper gestire l’operazione di trascinamento; per sem-plicità realizziamo solo il codice relativo all’eventodi movimento che invoca di conseguenza il meto-do touchesMoved: withEvent, gli altri in questo casonon sono necessari.

- (void)touchesMoved:(NSSet *)touches

withEvent:(UIEvent *)event

{

UITouch *theTouch = [touches anyObject];

CGPoint newPosition = [theTouch

locationInView:self.view];

if ([self.view hitTest:newPosition withEvent:event]

== red){

red.center = newPosition;

}

else if ([self.view hitTest:newPosition

withEvent:event] == green){

green.center = newPosition;

}

}

Il funzionamento è abbastanza intuitivo: primaotteniamo la posizione del tocco, rappresentata dalsolito CGPoint, successivamente dobbiamo deci-dere se questo punto è occupato da uno dei nostridue UIView: Per effettuare tale operazione interro-ghiamo la view principale, self.view, quella checontiene le due UIView figlie, invocando su di que-sta il metodo hitTest; hitTest è un metodo che resti-tuisce come risultato quale UIView si trova in undeterminato punto: basterà quindi verificare chequesta corrisponda alla view chiamata red o green,per decidere su quale stia avvenendo il tocco. Perspostare la view selezionata basterà cambiarne ilcentro, utilizzando la proprietà center (sempre ditipo CGPoint), attribuendole valore pari alla posi-zione del tocco. Una soluzione alternativa al codiceproposto per venire a conoscenza di quale view è almomento toccata consiste nell’utilizzare il metodotouchesForView: fornito da UIEvent. Abbiamo cosìmostrato i vari modi di interpretare l’interazionedell’utente in base al numero ed al tipo di touch.

Andrea Leganza

Novembre 2003/ 59 G

Gestione dell’interfaccia: i comandi touch � MOBILE

Aprile 2010/ 59 G

Fig. 7: Il sistema di coor-dinate dell’iPhone

Fig. 8: L’operazione di associazione tra IBOutlet eUIView

L’AUTORE

Andrea LeganzaLaureato in IngegneriaInformatica, da oltre undecennio realizza soluzionimultimediali, e non, supiattaforme e con linguaggidiversi. Certificato AdobeACE - Adobe Flex 3 and AIRCertified Expert, e EUCIPCore, appassionato difotografia, linguagiapponese e istruttore dinuoto FIN, è attualmenteimpegnato in numerosiprogetti multimediali,anche con iPhone, conalcune società nazionali edinternazionali; ècontattabile [email protected] odirettamente sul sitowww.leganza.it.

055-059:088-093-corsi-xsl 4-03-2010 14:34 Pagina 59

Page 68: 86746175 iPhone Programming

iPhone programming 68

iPhone programming Realizziamo un feed reader per iPhone

UN RSS READER SU IPHONEDISTRIBUIRE INFORMAZIONI ATTRAVERSO LE NOSTRE APPLICAZIONI PUÒ ESSERE MOLTO PIÙSEMPLICE ADOPERANDO LA CLASSE NSXMLPARSER. SAREMO COSÌ IN GRADO DI RACCOGLIERE E MOSTRARE I CONTENUTI DEGLI RSS CONSUMANDO POCHISSIMA BANDA

L’evoluzione apportata dal web negli ultimidue decenni nel campo della comunica-zione, ha reso possibile l’accesso a

migliaia di fonti informative, eterogenee sia intermini di contenuti ma anche di presentazionedelle informazioni. Verso la fine del millennio èmaturata la necessità di definire un formatounico che consentisse l’interscambio di tali datitra software e computer eterogenei ma che fosseanche facilmente leggibile ed analizzabile da unessere umano: da questa realtà ha preso formal’XML.

COM’È FATTO UN DOCUMENTO XMLAll’interno di un file XML prendono posto unoo più tag, entità testuali chiamate elementi,delimitate dal simbolo minore (<) e maggiore(>) che contengono le informazioni associatea tale marcatore. Ogni tag può essere del tipo<tag>contenuto</tag> oppure <tag />, puòcontenere al suo interno altri tag in numeroteoricamente illimitato, e può inoltre averedei valori associati, chiamati attributi cheprendono posto subito dopo il nome del tag,prima del simbolo “>”; ecco un esempio in cuivengono adoperate tutte queste caratteristi-che:

<?xml version="1.0"?>

<note>

<nota>

<autore>Andrea</autore>

<titolo>Ricordarsi di...</titolo>

<corpo>Ricordarsi di inviare una mail.

</corpo>

<foto url=”andrea.png” width=”160”

height=”220”/>

</nota>

<nota>

<autore>Andrea</autore>

<titolo>Comprare...</titolo>

<corpo>Comprare cavo di rete 10-100-

1000</corpo>

<foto url=”rete.png” width=”320”

height=”480” />

</nota>

<nota>

</nota>

</note>

La struttura di un file XML è a discrezionedello sviluppatore, anche per quanto riguardai nomi dei tag. È possibile impostare delleregole che consentono di limitare il tipo, ilnumero e i valori contenuti nei suoi elementi,adoperando DTD o XML Schema. La descri-zione qui fatta dell’XML è estremamente sem-plificata e le migliaia di risorse disponibilionline comprese le decine di libri pubblicatirendono certamente maggiore giustizia a que-sto formato del W3C. Uno dei maggiori difettiche gli vengono attribuiti è la sua prolissità,che ha spinto molti sviluppatori ad optare perJSON (JavaScript Object Notation). Con il pro-liferare di siti e blog ha preso vita il formatoora conosciuto come Really SimpleSindication, RSS, inizialmente nato con ilnome di RDF Site Summary negli uffici diNetscape. L’RSS (come il suo concorrente,Atom) non è nient’altro che un file XML strut-turato con delle regole precise, il cui scopo èquello di organizzare e rendere fruibili all’e-sterno di un sito web le informazioni che ven-gono pubblicate al suo interno senza richie-dere la consultazione ripetuta delle sue pagi-ne. Questo formato è presente in milioni disiti, anche merito dell’utilizzo di piattaformedi blogging e CMS che ne hanno automatizza-to la creazione, e l’icona con sfondo arancio-ne e righe bianche è ormai divenuta un sim-bolo universalmente riconosciuto per identi-

ht tp ://www. ioprogrammo. i t Giugno 2010/ 71 G

Realizziamo un feed reader per iPhone � MOBILE

❑ CD ❑ WEBiPhone_12.zip

cdrom.ioprogrammo.it

Conoscenze richiesteObjectivive-C

SoftwareMacOS X 10.5.4 o superiore

Impegno

¥Tempo di realizzazione

REQUISITI

Fig. 1: La visualizzazionedei titoli dei feed

071-075:088-093-corsi-xsl 6-05-2010 16:57 Pagina 71

Page 69: 86746175 iPhone Programming

69 iPhone programming

iPhone programmingRealizziamo un feed reader per iPhoneMOBILE � Realizziamo un feed reader per iPhone

ht tp ://www. ioprogrammo. i tG 72 / Giugno 2010

ficarlo. Il pregio di tale formato risiede anchenel fatto che consente di ridurre il traffico datidei siti, poiché un software che adopera RSSper verificare se i suoi contenuti sono statiaggiornati richiede un minore scambio di byterispetto a quello necessario per effettuare ilcaricamento completo necessario per visua-lizzare interamente le pagine web (costituiteda HTML, Javascript e immagini). A differenzadell’XML, in RSS alcuni campi sono obbliga-tori e nel prossimo paragrafo avremo modo diapprofondire questo discorso. Un aggregato-re, o RSS reader, è quel software che consentedi gestire e consultare tali file RSS. In questoarticolo e nel prossimo andremo a realizzareun RSS reader, adopereremo a tale scopo laclasse NSXMLParser, presentando i titolisequenzialmente in una tabella, e mostrere-mo i singoli contenuti in base alla selezioneeffettuata. NSXMLParser è un parser, unaclasse dedicata all’analisi di file XML, di tipoSAX, realizzato in Objective-C e disponibileall’interno dell’SDK.

IL FILE RSSEcco un esempio di come può essere struttu-rato un RSS versione 2.0:

<?xml version="1.0"?>

<rss version="2.0">

<channel>

<title></title>

<link></link>

<description></description>

<language></language>

<pubDate></pubDate>

<lastBuildDate></lastBuildDate>

<item>

<title></title>

<link></link>

<description></description>

<pubDate></pubDate>

</item>

</channel>

</rss>

All’interno del tag rss ne è contenuto un altrochiamato channel, che presenta prima unaserie di campi che descrivono le caratteristi-che generali del feed, come titolo, link ad unarisorsa web, generalmente questo campo con-tiene il sito a cui è associato il file, trova poi

posto la descrizione del file, la lingua, la datadi pubblicazione, e successivamente si avran-no uno o più tag item che rappresentano lesingole notizie. Ogni item può contenerediversi campi, tra cui il titolo, il link alla pagi-ne completa, la descrizione, la data di pubbli-cazione. Quelli mostrati sono sono alcuni deitag disponibili, ma nessuno di questi è obbli-gatorio, a parte title, link e description perquanto riguarda la descrizione del feed, e titleo description per il contenuto del singoloitem. Consultando le specifiche relative alla versio-ne utilizzata è possibile realizzare un feedcompliant con tale documentazione, avendogaranzia che sarà accettato dalla maggiorparte dei feed reader.

SAX E DOMQuando si adopera un parser per analizzareun file XML/RSS, esistono principalmentedue approcci per accedere ai suoi contenuti:uno è quello basato su SAX, l’altro su DOM.Un parser che adopera SAX, acronimo diSimple API for XML, si basa su un certo nume-ro di metodi (viene detto di tipo event-driven)che vengono invocati quando la libreriaincontra determinati elementi del file XML,all’interno di tali metodi è quindi cura delprogrammatore gestire i contenuti incontratie memorizzarli in una struttura dati che verràpoi mostrata all’utente; l’analisi del docu-mento è quindi di tipo sequenziale, partendodalla radice del file giungendo fino all’ultimoramo. L’approccio che adopera invece il DOM(Document Object Model, giunto alla versionechiamata “Level 3”), è del tipo tree-based,poichè analizza una copia completa dellastruttura del file XML, la cui struttura èappunto ad albero, mantenendola interamen-te in memoria. Un parser di tipo SAX vienedefinito streaming parser proprio per il fattoche attraversa e analizza la struttura insequenza, e si rivela proprio per questa carat-teristica come la soluzione migliore quando sianalizzano file di dimensioni relativamentegrandi; se il proprio obiettivo è quello di nonoccupare eccessiva memoria conviene optareper SAX, mentre si può scegliere indifferente-mente per l’uno o l’altro approccio quando siparsano documenti di modeste dimensioni.Perchè allora non scegliere sempre una libre-ria che adopera SAX? In alcune situazioni è necessario avere acces-so all’intera struttura del file immediatamen-

071-075:088-093-corsi-xsl 4-05-2010 17:27 Pagina 72

Page 70: 86746175 iPhone Programming

iPhone programming 70

iPhone programming Realizziamo un feed reader per iPhone

ht tp ://www. ioprogrammo. i t Giugno 2010/ 73 G

Realizziamo un feed reader per iPhone � MOBILE

te, come avviene per il DTD, adoperato pervalidare un documento, l’XSLT, utilizzato perapplicare trasformazioni/conversioni didocumenti XML, e XPath, utilizzato per acce-dere ai contenuti dell’XML, e in questi casi ilDOM si presenta come la soluzione ideale,sempre a condizione di non avere strutturedati di dimensioni eccessive. Per concludere, citiamo un ultimo pregio del-l’approccio SAX: nel caso ci trovassimo nellasituazione di dover prelevare un file XML dimedie/grandi dimensioni da una sorgenteremota (web o intranet) per ottenere una pre-cisa informazione, grazie alla natura stessa ditale tipo di parser potremo interrompere ildownload della risorsa quando avremo rag-giungo il tag desiderato, ciò comporterà unrisparmio di traffico che in alcuni casi potreb-be essere rilevante. Con l’approccio DOM ciònon è possibile, poiché il file deve sempreessere trasferito e memorizzato completa-mente sul computer locale prima di effettuarequalunque analisi.

LIBRERIE A CONFRONTONonostante in questo articolo andremo adutilizzare solo la classe NSXMLParser è oppor-tuno sapere che ne esiste un’altra, utilizzabilesia con SAX che DOM, realizzata in linguaggioC e chiamata libXML2 fornita nell’SDK (natain seno al progetto Gnome), il cui utilizzoperò si rivela sicuramente meno intuitivorispetto alla prima, ma le cui prestazioni sononettamente superiori, spesso di un ordine digrandezza, merito ovviamente del linguaggioa più basso livello che è stato utilizzato. Afianco di queste due soluzioni ne trovano poiposto altre, disponibili nel web che si propon-gono come un’alternativa fornendo unapproccio solamente tramite DOM:

• TBXML: libreria di tipo DOM con approccioleggero (in termini di risorse), realizzata inObjective-C, non effettua la validazione ,non supporta XPath, e supporta solo la let-tura del file XML, non la modifica.

• TouchXML: libreria di tipo DOM che mimal’approccio di NSXMLParser, realizzata inObjective-C, supporta XPath ma comeTBXML è solo per lettura di file XML.

• KissXML: libreria di tipo DOM che mimal’approccio di NSXMLParser, realizzata inObjective-C, basata su TouchXML, alla quale

aggiunge la possibilità di modificare l’XML.

• TinyXML: libreria realizzata in C di tipoDOM, supporta lettura e scrittura di fileXML, ma non XPath, funzionalità imple-mentabile adoperando un’altra libreriaassociata chiamata TyinyXPath.

• GDataXML: libreria di tipo DOM che mimal’approccio di NSXMLParser, realizzata inObjective-C, sviluppata da Google, suppor-ta lettura e scrittura di file XML e XPath.

Per quanto riguarda la scelta tra le due libreriefornite all’interno dell’SDK, NSXMLParser elibXML2, questo dipende dalle dimensioni deifile XML coinvolti, ma anche dalla propriaesperienza nel linguaggio C, obbligatorioquando si opta per la seconda libreria; nelcaso di indecisione si possono testare le pre-stazioni delle due librerie sui propri fileXML/RSS modificando un progetto fornitoall’interno della documentazione presente sianel sito Apple che allegata in XCode, il cuinome è XMLPerformace, raggiungibile nellasezione “Related sample code” dellaNSXMLParser Class Reference. Anche selibXML2 è ineguagliabile per la velocità concui analizza un documento resta sempre daconsiderare la necessità di adoperare il lin-

Fig. 2: Un test effetuato analizzando le top 300 songs diitunes mostra le differenti prestazioni delle due libreriefornite nell’SDK

SPECIFICHE XML,RSS 1.X E 2.XPer mantenersi aggiornatisulle specifiche riguardantiXML ed RSS, conviene fareaffidamento sui siti ufficiali:

www.w3.org/XML/

http://web.resource.org/rss/1.0/spec

http://cyber.law.harvard.edu/rss/rss.html

NOTA

071-075:088-093-corsi-xsl 4-05-2010 17:27 Pagina 73

Page 71: 86746175 iPhone Programming

71 iPhone programming

iPhone programmingRealizziamo un feed reader per iPhoneMOBILE � Realizziamo un feed reader per iPhone

ht tp ://www. ioprogrammo. i t

G 74 / Giugno 2010

guaggio C, che per alcuni utenti si rivela loscoglio maggiore. Se invece si vuole adoperareDOM la scelta può cadere su qualunque dellecinque librerie precedentemente descritte sesi vuole solo analizzare il documento XML,mentre il cerchio si restringe in caso si doves-se adoperare XPath oppure fosse necessariomodificare il file. Da test empirici è risultatoche TBXML si è rivelata una delle migliori intermini di velocità e di occupazione di memo-ria, ma per il fatto che molte librerie vengonomodificate costantemente questa affermazio-ne potrebbe già essere invalidata mentre leg-gete queste righe; comunque per documentidi dimensioni medio-grandi, quando la velo-cità di analisi del documento è di primariaimportanza, il suggerimento è di adoperarelibxml2 (approccio SAX), mentre convieneoptare per TBXML per un approccio DOM, ilcui pregio in confronto a libxml2 (approccioDOM) è ovviamente la possibilità di adoperarel’Objective-C. Un’ultima nota che potrebbeinteressare chi desidera analizzare anche pagi-ne HTML con tali parser: con tutte questelibrerie è possibile analizzare anche pagineweb di tipo XHTML o HTML 5, poichè entram-bi i formati sono applicazioni dell’XML.

IL PROGETTOCreiamo un nuovo progetto, di tipo “UtilityApplication”, chiamandolo rssparser, modifi-chiamo la MainView inserendo al suo internoun UITableView (impostiamo delegate e data-source al view controller Mainviewcontroller.htramite interface builder), al quale accedere-mo attraverso un IBOutlet inserito all’internodel file MainViewController.h, che chiamere-mo rssTable, ricondandoci sempre di invocareil release all’interno del metodo dealloc perevitare leaks. Rimuoviamo il pulsante posizio-nato automaticamente in basso a destra conl’icona “i” perchè non è necessario in questoprogetto. Decommentiamo il metodo viewDidLoadall’interno di MainviewController.c e inseria-mo il seguente codice:

NSURL *theURL = [NSURL URLWithString:@

"http://rss.cnn.com/rss/edition.rss"];

NSXMLParser *parser = [[NSXMLParser alloc]

initWithContentsOfURL:theURL];

[parser setDelegate:self];

BOOL parseResult = [parser parse];

NSLog(@"Risultato del parsing della risorsa :

%@", parseResult?@”completato”:@”errore”);

Con queste poche righe di codice abbiamo giàconfigurato ed avviato il parser. Nella primariga abbiamo creato un oggetto di tipo NSURLcontenente il link al file RSS, nella seconda èstata istanziata ed inizializzata una variabiledi NSXMLparser, successivamente abbiamoimpostato il suo delegate alla classe correntein modo da ricevere tutti gli eventi che ver-ranno generati dal parser durante la sua navi-gazione all’interno del file. Infine, invochiamoil metodo parse che dà inizio all’analisi deldocumento e mostriamo il risultato dell’even-to nella console di debug tramite NSLog. Orache abbiamo implementato il blocco di codi-ce responsabile dell’avvio del parsing apria-mo una parentesi sui metodi che il parserinvierà alla nostra classe, che svolge il ruolo didelegate.

IL FUNZIONAMENTO DI NSXMLPARSERUtilizziamo un file XML molto semplice, con-tenente come informazioni la versione, unarticolo e una descrizione associata

<?xml version= "1.0" encoding="UTF8">

<articolo autore="Andrea Leganza">

<titolo>Un feed reader per tutti</titolo>

<descrizione>Articolo dedicato ai file

XML.</descrizione>

</articolo>

per mostrare come si comporta un’istanzadella classe NSXMLParser, descrivendo lasequenza degli eventi inviati al suo delegate,che deve rispettare il protocollo richiestochiamato NSXMLParserDelegate:

1. Avvio del parsing del documento;2. Trovato inizio del tag articolo con attributo

autore e valore “Andrea Leganza”;3. Trovato inizio del tag titolo;4. Trovati caratteri all’interno del tag titolo:

“Un feed reader per tutti.”;5. Trovata fine del tag titolo;6. Trovato inizio del tag descrizione;7. Trovati caratteri all’interno del tag

descrizione: “Articolo dedicato ai fileXML.”;

8. Trovata fine del tag descrizione;9. Trovata fine del tag articolo;10. Fine del parsing del documento.

I passi 1 e 10 avvengono una sola volta duran-

NOTA

LE RISORSE PER IPHONE

Creazione dell'account, perscaricare l'SDK e

consultare ladocumentazione:

http://developer.apple.com/iphone

071-075:088-093-corsi-xsl 4-05-2010 17:27 Pagina 74

Page 72: 86746175 iPhone Programming

iPhone programming 72

iPhone programming Realizziamo un feed reader per iPhone

ht tp ://www. ioprogrammo. i t

Realizziamo un feed reader per iPhone � MOBILE

Giugno 2010/ 75 G

te la fase di parsing, mentre quelli di ini-zio/fine elemento (parser:didStartElement* eparser:didEnd Element*) una volta per ognitag incontrato; come si nota l’analisi procedeandando sempre più in profondità, per poirisalire quando trova i vari tag di chiusura. Seavessimo inserito una sequenza di n tag arti-colo, avremo avuto n ripetizioni della sequen-za dei passi da 2 a 9. Passiamo ora dalla ver-sione con pseudocodice alla sequenza dichiamate dei metodi effettivamente richiestial delegate:

1. parserDidStartDocument:

1. parser:didStartElement:namespaceURI:

qualifiedName:attributes: //trovato tag articolo

1. parser:didStartElement:namespaceURI:

qualifiedName:attributes: //trovato tag titolo

2. parser:foundCharacters:

//trovato contenuto per tag titolo

3. parser:didEndElement:namespaceURI:

qualifiedName: //fine tag titolo

4. parser:didStartElement:namespaceURI:

qualifiedName:attributes: //trovato tag

descrizione

5. parser:foundCharacters:

//trovato contenuto per tag descrizione

6. parser:didEndElement:namespaceURI:

qualifiedName: //fine tag descrizione

7. parserDidEndDocument:

Oltre a questi ne esistono ovviamente moltialtri, come quelli scatenati quando il parserincontra caratteri particolari, tipi di informa-zioni non prettamente testuali (CDATA), com-menti, o il DTD; per avere una visione com-pleta di tutti gli eventi che il delegate può rice-vere basta consultare la documentazione rela-tiva a NSXMLParser Delegate. L’ultimo metodo che è sempre opportunoimplementare in aggiunta ai precedenti èquello invocato dal parser per identificare egestire le situazioni di errore, chiamato pars-er:parseErrorOccurred:. Un errore può presen-tarsi per diversi motivi, come tag non chiusi,oppure caratteri non accettati, in pratica intutte quelle situazioni in cui il parser è indeci-so sul da farsi perché ha incontrato una strut-tura non coerente. All’interno del metodoparser:parse ErrorOccurred si può deciderecome procedere dopo aver interrogato

l’istanza di NSError chiamata parseError i cuicodici di errore sono elencati in una legendapresente nella sezione Parser Error Constantsdella documentazione associata alla classeNSXMLParser.

IL NUMERO DI INVOCAZIONILa sequenza di eventi descritti nel precedenteparagrafo per il file XML mostrato è specularea quella per un file RSS ovviamente con alcu-ne differenze in termini di numeri: in questocaso il numero di invocazioni di pars-er:didStart Element:... (e del complementareparser:didEndElement:...) è nettamente supe-riore: otterremo infatti una sequenza di even-ti dovuta alla parte di descrizione del feed, esuccessivamente, quando il parser giungeràalle notizie, identificate dai tag item, in nume-ro pari a molte decine, anche centinaia. Per valutare il numero complessivo di invoca-zioni di metodi richieste al delegate senzaavviare il software, basandosi solo sulla strut-tura del file RSS, basterà calcolare il risultatodella seguente formula: S+(K*2)+(n*2)+(n*(j*2))+d dove

• S: costante pari a 2, i due metodi di inizio efine documento

• K: costante, numero di tag utilizzati per ladescrizione del feed;

• n: numero di notizie (tag item)j: numero di tag presenti all’interno dellasingola notizia (tag item);

• d: numero di invocazioni di foundCharacters:

È ovviamente possibile calcolare tutti questiparametri inserendo una variabile che conta-bilizzi il numero di invocazioni di ogni meto-do quando si avvia un parsing. Questi calcoli possono risultare utili quandosi è al contempo consumatore (tramite ilsoftware iPhone dell’RSS), ma anche respon-sabile della sua generazione. In tal modo sipossono apportare modifiche strutturali pervelocizzarne l’analisi, riducendo accapo ecaratteri inutili, oppure semplificando lastruttura accorpando tag ed adoperando attri-buti. Nel prossimo articolo tratteremo approfondi-tamente del prelievo delle informazioni all’in-terno dei tag e mostreremo la loro descrizionecon le informazioni associate. Buona pro-grammazione.

Andrea Leganza

L’AUTORE

Andrea LeganzaLaureato in IngegneriaInformatica, da oltre undecennio realizza soluzionimultimediali, e non, supiattaforme e con linguaggidiversi. Certificato AdobeACE - Adobe Flex 3 and AIRCertified Expert, e EUCIPCore, appassionato difotografia, linguagiapponese e istruttore dinuoto FIN, è attualmenteimpegnato in numerosiprogetti multimediali,anche con iPhone, conalcune società nazionali edinternazionali; ècontattabile [email protected] odirettamente sul sitowww.leganza.it.

071-075:088-093-corsi-xsl 4-05-2010 17:27 Pagina 75

Page 73: 86746175 iPhone Programming

73 iPhone programming

iPhone programmingiPhone parsing RSSMOBILE iPhone parsing RSS

http://www.ioprogrammo.it 48 / Luglio 2010

Nell’articolo precedente abbiamo trattato in maniera teorica del parsing di feed RSS, descrivendo a grandi linee il formato RSS,

ciò ha permesso di mostrare come questo sia sem-plicemente un file XML con una precisa struttura; in un secondo momento sono stati descritti i due tipi di parser forniti nell’SDK, DOM e SAX, dei quali abbiamo spiegato pregi e difetti; successivamente siamo entrati nel vivo del corso mostrando il funzio-namento del parser NSXMLParser, il quale invoca sequenzialmente alcuni metodi che devono essere implementati dal suo delegate, comportamento che viene definito event-driven. In questa ultima parte mostreremo il contenuto dei metodi del delegate e ne spiegheremo il funzionamento.

IL PARSING DEL FILE RSSRiproponiamo per comodità il metodo viewDidLo-ad nel quale diamo inizio all’operazione di parsing:

- (void)viewDidLoad{

//Allochiamoedinizializziamol’arraycheconterràle

singolenews;

notizie=[[NSMutableArrayalloc]init];

NSURL*theURL=[NSURLURLWithString:@”http://rss.

cnn.com/rss/edition.rss”];

NSXMLParser*parser=[[NSXMLParseralloc]

initWithContentsOfURL:theURL];

[parsersetDelegate:self];

BOOLparseResult=[parserparse];

NSLog(@”Risultatodelparsingdellarisorsa:%@”,pars

eResult?@”completato”:@”errore”);

}

Spieghiamo brevemente il codice appena proposto: abbiamo creato un’istanza di NSURL nella quale è stato inserito l’URL del feed che vogliamo analiz-zare, successivamente istanziamo un oggetto della

classe NSXMLParser impostandone come delegate la classe in cui questo codice è presente; infine, diamo inizio all’operazione di parsing. Ora che abbiamo dato inizio all’operazione di parsing sap-piamo, dall’articolo precedente, che avremo una sequenza di chiamate ai seguenti metodi:

parserDidStartDocument:(1chiamata)

parser:didStartElement:namespaceURI:qualifiedName:

attributes:(nchiamate)

parser:foundCharacters:(mchiamate)

parser:didEndElement:namespaceURI:qualifiedName:

(nchiamate)

parserDidEndDocument:(1chiamata)

ParserDidStartDocument e parserDidEndDocument indicano l’inizio e il termine della procedura di par-sing e generalmente sono adoperati per inizializza-re e deallocare se necessario, tutte quelle variabili e strutture dati che verranno utilizzate durante la fase di analisi del documento RSS. parser:didStartElement:namespaceURI:qualifiedName:attributes: e par-ser:didEndElement:namespaceURI:qualifiedName: svolgono invece un ruolo molto importante, infatti ci informano quando è stato trovato un tag, aperto (<tag>) o chiuso (</tag>). parser:foundCharacters: viene invocato quando trova del testo tra un tag di apertura e uno di chiusura (<tag>testo</tag>). parser:foundCharacters viene invocato quando il parser incontra del testo all’interno di un tag. All’interno di questi metodi dovremo popolare una struttura dati apposita con le informazioni ricevute per ogni tag, che andremo poi a inserire all’interno di un array: al termine del parsing troveremo in tale array per ogni suo record tutti i dati associati al sin-golo tag incontrato. Generalmente l’operazione di parsing necessita delle seguenti strutture dati:

•una istanza della classe NSMutableString, curren-tElement, per condividere tra le varie chiamate dei metodi il tag che stiamo analizzando;

•n istanze della classe NSMutableString; nel nostro caso sono quattro, currentFitle, currentDate, cur-

REQUISITI

Conoscenze richiesteObjective-C

SoftwareMac OS 10.5.x o superiore (10.6.x per l’SDK 3.x)

Impegno

Tempo di realizzazione

❑ CD ❑ WEBiPhone_RSS.rar

cdrom.ioprogrammo.it

UN RSS READER PER IPHONEADOPERANDO LA CLASSE NSXMLPARSER, POSSIAMO INTERAGIRE CON I CONTENUTI DI UN FEED RSS. IN QUESTO ARTICOLO MOSTREREMO COME FARE E COME IMPLEMENTARE UNA STRUTTURA DATI PER LA VISUALIZZAZIONE DELLE INFO RICEVUTE

Page 74: 86746175 iPhone Programming

iPhone programming 74

iPhone programming iPhone parsing RSS MOBILEiPhone parsing RSS

Luglio 2010 / 49 http://www.ioprogrammo.it

rentSummary e currentLink, ognuna conterrà il testo associato al relativo tag;

•una istanza del tipo NSMutableDictionary, con il nome notizia, che conterrà tutte le informazioni che verranno trovate per una singola news e associate alle NSMutableString (titolo, data, som-mario, link).

•un array del tipo NSMutableArray, notizie, nel quale andremo ad inserire al termine del parsing di un tag di tipo item l’NSMutableDictionary appena popolato; tale istanza quindi alla fine del parsing conterrà n campi, uno per ogni news prensente nell’RSS.

NSMutableArray*notizie;

NSMutableDictionary*notizia;

NSMutableString*currentElement;

NSMutableString*currentTitle;

NSMutableString*currentDate;

NSMutableString*currentSummary;

NSMutableString*currentLink;

Perché adoperare istanze di NSMutableString inve-ce di NSString? La flessibilità di NSMutableString ci consente di non dover allocare ed inizializzare ogni volta che troviamo un nuovo titolo, o data, o sommario o link, una nuova variabile, possia-mo infatti rimuovere la stringa che tali variabili contengono semplicemente impostando il loro testo a @””, questa operazione avverrà quando avremo raggiunto il tag di chiusura e stiamo per passare ad analizzarne un altro. È importante ricordare che le istanze di NSString sono costanti e non abbiamo controllo su quando verranno deallocate, poichè durante il parsing ne verreb-bero generate centinaia, non abbiamo modo di sapere se e quando queste verranno rimosse dalla memoria, situazione invece non presente con NSMutableString, classe che ci consente pieno controllo sul suo tempo di vita grazie all’invoca-zione di release. Prima di iniziare con metodi che si rivelano sicuramente più interessanti è impor-tante porre l’attenzione al caso in cui per qualche motivo il parser fallisca l’operazione di analisi, i motivi possono essere molteplici, uno dei più fre-quenti può presentarsi quando non c’è sufficiente copertura sulla rete GSM, oppure non è presente una la rete WIFI, situazioni quindi che rendono impossibile connettersi al server in fase di richie-sta della risorsa oppure in ricezione del file RSS se la connessione si interrompe; qualunque sia il tipo di errore questo interromperà il parsing, e nel caso in cui sia stato implementato il metodo parseErrorOccurred: sarà responsabilità del par-ser provvede ad invocarlo. È sempre consigliabile

implementare tale metodo, presentando con un UIAlerView la causa dell’errore, informazione disponibile con un codice numerico all’interno del parametro parseError, in questo modo sarà anche più semplice ricevere segnalazioni da parte degli utenti in caso di un non corretto funzio-namento di questo componente. Per un elenco completo dei quasi 100 codici di errore possi-bili è necessario consultare la documentazione associata alla classe NSXMLParser, nella sezione chiamata Parser Error Constants.

-(void)parser:(NSXMLParser*)parser

parseErrorOccurred:(NSError*)parseError{

NSLog(@”Errorediparsing:%f”,[parseErrorcode]);

UIAlertView*errorAlertView=[[UIAlertViewalloc]

initWithTitle:@”Erroredurantel’analisi“

message:[NSStringstringWithFormat:@”Erroredi

parsing:%f”,[parseErrorcode]]

delegate:nil

cancelButtonTitle:@”ok”

otherButtonTitles:nil];

[errorAlertViewshow];

[errorAlertViewrelease];

}

Ora bisogna ricordare quale sia la struttura più comune di un file RSS:

<?xmlversion=”1.0”?>

<rssversion=”2.0”>

<channel>

<title></title>

<link></link>

<description></description>

<language></language>

<pubDate></pubDate>

<lastBuildDate></lastBuildDate>

<item>

<title></title>

<link></link>

<description></description>

<pubDate></pubDate>

...//possibilialtriparametri

</item>

...//ripetizionidi<item>...</item>

</channel>

</rss>

Inizializziamo all’interno del metodo parserDid-StartDocument prima la stringa che conterrà il nome del tag che il parser ha incontrato, e facciamo lo stesso con le stringhe che conterranno i testi trovati all’interno dei vari tag di ogni item; i tag che ci interessano sono il titolo, la data, il sommario e il link alla pagina web: title, pubDate, description e link:

Fig. 1: Il risultato finale di questo progetto

Page 75: 86746175 iPhone Programming

75 iPhone programming

iPhone programmingiPhone parsing RSSMOBILE iPhone parsing RSS

http://www.ioprogrammo.it 50 / Luglio 2010

-(void)parserDidStartDocument:(NSXMLParser*)

parser{

NSLog(@”Parsingdeldocumentoiniziata.”);

currentElement=[[NSMutableStringalloc]init];

currentTitle=[[NSMutableStringalloc]init];

currentDate=[[NSMutableStringalloc]init];

currentSummary=[[NSMutableStringalloc]init];

currentLink=[[NSMutableStringalloc]init];

}

Deallocheremo tali variabili solo alla fine del par-sing, all’interno di parserDidEndDocument: e prov-vederemo infine ad aggiornare la tabella che adope-riamo per mostrare i titoli dei feed RSS.

-(void)parserDidEndDocument:(NSXMLParser*)parser{

[currentElementrelease];

[currentTitlerelease];

[currentDaterelease];

[currentSummaryrelease];

[currentLinkrelease];

NSLog(@”Finedelparsing.”);

NSLog(@”Notiziecontiene%delementi”,[notizie

count]);

//RefreshdeicontenutidellaUITableView

[rssTablereloadData];

}

Dopo aver inizializzato la maggior parte delle varia-bili necessarie per il parsing, entriamo nel vivo del codice, analizzando parser: didStartElement: name-spaceURI: qualifiedName: attributes:

-(void)parser:(NSXMLParser*)parser

didStartElement:(NSString*)elementNa

menamespaceURI:(NSString*)namespa

ceURIqualifiedName:(NSString*)qName

attributes:(NSDictionary*)attributeDict{

[currentElementsetString:[elementNamecopy]];

if([currentElementisEqualToString:@”item”]){

notizia=[[NSMutableDictionaryalloc]init];

}

}

Appena il parser ci notifica che ha trovato un tag di apertura, accessibile interrogando il parame-tro elementName, lo memorizziamo all’interno di currentElement; come avevamo mostrato prima, la struttura di un file RSS presenta molti tag diversi, description, language, pubDate, lastBuildDate e tanti altri, a noi interessano solo i tag <item>, con i tag “figli” <title>, <link>, <description>, <pubDate>, che contengono le singole notizie, e per tale motivo pre-leveremo i dati dall’RSS solo quando ci troveremo all’interno di tali marcatori. Ecco svelato l’utilità della variabile currentElement, questa ci servirà nel meto-

do parser: foundCharacters per popolare le nostre NSMutableString e l’NSDictionary solo quando siamo all’interno di tali tag, infatti parser: foundCha-racters non fornisce alcun parametro che indichi in che tag siamo spetta quindi a noi tenerne traccia; adoperiamo quindi un semplice confronto tra strin-ghe [elementName isEqualToString:@”tagname”] per capire se il tag corrente è di interesse o no. Nel caso in cui siamo all’interno del tag <item> provvedia-mo a inizializzare un NSMutableDictionary al cui interno inseriremo i contenuti di title, link, descrip-tion e pubDate. Ad una prima invocazione didStar-tElement: fornirà come elementName @”title”, alla seconda invocazione @”title”, poi @”description” ed infine @”pubDate”. Un’ultima nota riguarda il parametro attributeDict, questo è un NSDictionary che contiene tutti gli attributi trovati per un tag, ad esempio, se il tag che abbiamo incontrato è del tipo <tag name=”Andrea” second=”Leganza” age=”31”>Informatic Engineerer</tag> invocando su tale variabile il metodo objectForKey: adoperando come chiave il nome degli attributi presenti otterre-mo il valore di tali proprietà.

[attributeDictobjectForKey:@”name”];restituiscela

stringa@”Andrea”

[attributeDictobjectForKey:@”second”];restiuiscela

stringa@”Leganza”

[attributeDictobjectForKey:@”age”];restiuiscela

stringa@”31”

Adesso che siamo all’”interno” di un tag e possono presentarsi due situazioni: è presente un ulteriore tag di apertura, come avviene per <item>, con i suoi tag “figli”, nel qual caso verrà invocata un’altra volta didStartElement:, oppure, come avviene quando siamo in uno qualsiasi dei tag “figli”, verrà invocato parser: foundCharacters: ad indicare che abbiamo incontrato del testo all’interno del marcatore, del tipo <tag>testo_contenuto</tag>:

-(void)parser:(NSXMLParser*)parser

foundCharacters:(NSString*)string{

if([currentElementisEqualToString:@”title”]){

[currentTitleappendString:string];

}elseif([currentElementisEqualToString:@”link”]){

[currentLinkappendString:string];

}elseif([currentElementisEqualToString:@”descripti

on”]){

[currentSummaryappendString:string];

}elseif([currentElement

isEqualToString:@”pubDate”]){

[currentDateappendString:string];

}

}

Come abbiamo appena detto, currentElement ci permette di identificare il tag in cui ci troviamo,

NOTA

RIFERIMENTI WEBCreazione dell’account,

per scaricare l’SDK e con-sultare la documentazione previa registrazione gratu-ita: http://developer.apple.

com/iphone/

Page 76: 86746175 iPhone Programming

iPhone programming 76

iPhone programming iPhone parsing RSS MOBILEiPhone parsing RSS

Luglio 2010 / 51 http://www.ioprogrammo.it

all’interno di parser:foundCharacters: popoliamo la NSMutableString in base al tag (title,link,description,pubdate) che è al momento analizzato. Quando raggiungiamo il tag di chiusura di ogni singolo tag avremo un’invocazione di didEndElement:, solo nel caso in cui abbiamo raggiunto la fine della news, identifcato dal tag di chiusura </item> provvediamo a popolare con le NSMutableString generate nel metodo foundCharacters l’NSMutableDictionary, ini-zializzato nel metodo didStartElement. Concludiamo rimuovendo l’NSMutableDictionary e rimuovendo il contenuto delle NSMutableString, così saranno pronte per essere di nuovo popolate per il prossimo item. Ricordiamo che nel caso in cui il tag svolges-se il ruolo di mero contenitore di altri tag, come avviene per <item>, non avremo alcuna chiamata a foundCharacters, quindi ipotizzando che questo tag contenga solo i 4 tag “figli” mostrati, avremo 4 invocazioni a foundCharacters e non 5 come alcuni potrebbero erroneamente pensare.

-(void)parser:(NSXMLParser*)

parserdidEndElement:(NSString*)element

NamenamespaceURI:(NSString*)namespaceURI

qualifiedName:(NSString*)qName{

if([elementNameisEqualToString:@”item”]){

[notiziasetObject:[currentTitlecopy]forKey:@”title”];

[notiziasetObject:[currentLinkcopy]forKey:@”link”];

[notiziasetObject:[currentSummarycopy]

forKey:@”summary”];

[notiziasetObject:[currentDatecopy]

forKey:@”date”];

[notizieaddObject:[notiziacopy]];

[notiziarelease];

[currentTitlesetString:@””];

[currentDatesetString:@””];

[currentSummarysetString:@””];

[currentLinksetString:@””];

}

}

LA POPOLAZIONE DELLA TABELLAOra che abbiamo finalmente ottenuto le informazioni che desideriamo le andremo a mostrare in una UITableView; provvediamo prima a configurare la tabella:

-(NSInteger)numberOfSectionsInTableView:

(UITableView*)tableView{

return1;

}

-(NSInteger)tableView:(UITableView*)tableView

numberOfRowsInSection:(NSInteger)section{

return[notiziecount];

}

popoliamo quindi il corpo del metodo responsabile della visualizzazione nelle singole celle/righe, tableView:cellForRowAtIndexPath:

-(UITableViewCell*)tableView:(UITableView*)table

ViewcellForRowAtIndexPath:(NSIndexPath*)indexPath

{

staticNSString*MyIdentifier=@”MyIdentifier”;

UITableViewCell*cell=[tableViewdequeueReusableCell

WithIdentifier:MyIdentifier];

if(cell==nil){

cell=[[[UITableViewCellalloc]initWithStyle:UITableVie

wCellStyleSubtitlereuseIdentifier:MyIdentifier]

autorelease];

cell.imageView.image=[UIImage

imageNamed:@”news.jpg”];

cell.textLabel.adjustsFontSizeToFitWidth=YES;

}

intnotiziaIndex=[indexPathindexAtPosition:[index

Pathlength]-1];

cell.textLabel.text=[[notizieobjectAtIndex:notizia

Index]objectForKey:@”title”];

returncell;

}

A parte le tipiche operazioni di configurazione, abbia-mo invocato il metodo adjustsFontSizeToFitWidth sulla textLabel della cella allo scopo di impostare l’autoresizing del testo, in modo da consentire una completa visualizzazione del titolo della news, evitan-do l’autotroncamento del testo, operazione effettuata

Fig. 2: Selezionando una cella usciremo dall’applicazione e apriremo il link con Safari

Page 77: 86746175 iPhone Programming

77 iPhone programming

iPhone programmingiPhone parsing RSSMOBILE iPhone parsing RSS

http://www.ioprogrammo.it 52 / Luglio 2010

L’AUTORE

Andrea Leganza Laureato in Ingegneria Informatica,

certificato Adobe ACE - Adobe Flex 3 and AIR Certified Expert, EUCIP

Core, Sun Certified Programmer for JAVA 6,

istruttore di nuoto FIN di 2° Livello, è attualmente

impegnato in numerosi progetti multimediali,

anche con iPhone e iPad, con alcune società nazio-

nali ed internazionali, è contattabile su [email protected] o su www.leganza.it.

per default da tale UILabel; otteniamo l’indice della notizia per la relativa cella e lo memorizziamo all’in-terno della variabile notiziaIndex, andiamo quindi immediatamente a utilizzare nella riga successiva tale variabile per ottenere il relativo oggetto contenuto nell’array notizie, e preleviamo il titolo della news uti-lizzando la chiave title dell’NSDictionary selezionato.

LA VISUALIZZAZIONE DELLE PAGINEOra che abbiamo presentato correttamente a video i tito-li, possiamo decidere di conseguenza il modo più adatto con cui rispondere alla selezione dell’utente; avremmo potuto utilizzare una UITextView o un UIWebView ma approfittiamo di questo contesto per esporre una nuova funzionalità fornita dall’SDK: l’apertura di link web uti-lizzando Safari. In uno dei primi articoli dedicati alla programmazione su iPhone abbiamo mostrato come realizzare un mini web browser adoperando la classe UIWebView, il cui cuore è lo stesso di Safari, l’open source WebKit, ma è possibile anche aprire link senza dover utilizzare tale classe, a patto di accettare l’inconve-niente di dover terminare la propria applicazione. Ogni applicativo espone al suo interno un’istanza condivisa (in gergo viene chiamata un singleton) appartenente alla classe UIApplication, ottenibile invocando su tale classe il metodo sharedApplication, tale istanza fornisce una quindicina di metodi, due dei quali sono quelli che più ci interessano in questo articolo: canOpenURL: e openURL:. Il primo metodo restituisce come risultato un booleano (YES/NO) a seconda se il link che gli inviamo è associato ad almeno un software installato nel nostro telefono (esiste quindi almeno un software in grado di gestirne i contenuti), mentre il secondo provvede ad aprire tale link con il programma associato. Safari, il web browser installato di default nell’ iPhone/iPod Touch, è imposta-to per default come software responsabile dell’apertura dei link del tipo http://, https:// e mailto:, quindi, quan-do andremo a chiamare canOpenURL passando il link associato alla news, otterremo una risposta affermativa, potremo invocare openURL con la sicurezza che l’utente visualizzerà la pagina della notizia associata all’interno di Safari. Immediatamente dopo l’invocazione di tale metodo assisteremo alla chiusura del nostro software e all’apertura di Safari. Questa pratica si rivela utile nei casi in cui non si vuole impegnare tempo nella costruzione di un browser web interno, ma ha il grande limite di termi-nare l’utilizzo del software, obbligando l’utente, nel caso lo desiderasse, di dover riaprire il software e rieffettuare tutte le operazioni necessarie per mostrare nuovamen-te le news: è quindi una soluzione da adoperare solo nelle primissime fasi di sviluppo del software, succes-sivamente è sempre opportuno realizzare un browser interno, operazione che impiega in genere poche decine di minuti.

-(void)tableView:(UITableView*)tableViewdidSelect

RowAtIndexPath:(NSIndexPath*)indexPath{

intnotiziaIndex=[indexPathindexAtPosition:[index

Pathlength]-1];

//Illinkvieneprelevatoadoperandolachiavelink

sull’NSDictionary.

NSString*notiziaLink=[[notizieobjectAtIndex:notizia

Index]objectForKey:@”link”];

//Persicurezzavengonosostituiti,sepresenti,caratteri

noncompatibiliconilformatoaccettattoperilinkcon

opportunesequenzecomeadesempio%20perrappre

sentarelospazio

notiziaLink=[notiziaLinkstringByAddingPercentEscapes

UsingEncoding:NSUTF8StringEncoding];

//Nelcasofosseropresentisimbolidiritornoacapo

vengonorimossi

...

sharedApplication consente non solo di aprire link a siti web, ma anche effettuare telefonate, inviare SMS, o anche inviare email, previa sempre confer-ma da parte dell’utente:

[[UIApplicationsharedApplication]openURL:[NSURLURL

WithString:@”tel://123456789”]]

[[UIApplicationsharedApplication]openURL:[NSURLURL

WithString:@”sms://123456789”]]

[[UIApplicationsharedApplication]openURL:[NSURLURL

WithString:@”mailto:emailAdress?subject=helloSubject&

body=hellomyfriendhasbeenawhile...”]];

La necessità di richiedere conferma quando si effettua una chiamata o si invia un SMS è per evitare abusi da parte di sviluppatori non proprio ortodossi, che potrebbero effettuare chiamate senza autorizzazione (potendo quindi ascoltare le conversazioni altrui), con conseguente spesa da parte dell’utente, oppure inviare SMS con dati sensibili senza che l’utente ne abbia controllo; ovviamente questi due metodi sono validi solo su iPhone e in questo caso se invocassimo il metodo canOpenURL su un iPod Touch otterremo risposta negativa. L’ultimo metodo, quello di invio email era una delle poche soluzioni disponibili prima del rilascio dell’SDK 3, ora grazie all’introduzione del compo-nente MFMailComposeViewController è sempre meno adoperato, e si possono applicare le stesse considerazioni fatte per l’apertura di link http/s con Safari. È anche possibile lanciare l’applicativo goo-gle maps installato nel dispositivo http://maps.goo-gle.com/<parametri>), e una pagina dell’App Store (http://phobos.apple.com/<parametri>). È possi-bile inoltre registrare la propria applicazione come responsabile dell’apertura di uno o più formati di link ma è adoperato solo in rari casi.

Andrea Leganza

Page 78: 86746175 iPhone Programming

www.punto-informatico.it

Questo approfondimento tematico è pensato per chi vuol imparare a programmare e creare software per l’Apple iPhone.La prima parte del testo guida il lettore alla conoscenza degli strumenti necessari per sviluppare sulla piattaforma mobile di Cupertino. Le sezioni successive sono pensate per un apprendimento pratico basato su esempi di progetto: la creazione di un browser su misura, la gestione dell’interfaccia, la programmazione di un’agenda e di una to do list, la gestione corretta di celle e tabelle, l’utilizzo dell’accelerometro, la progettazione di un RSS reader e via dicendo. Una serie di esempi pratici da seguire passo passo che – creando applicazioni testabili e perfettamente funzionanti - spingono il lettore a sperimentare sul campo il proprio livello di apprendimento e lo invitano a imparare divertendosi.

www.punto-informatico.it