Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente … · L'iPhone è un prodotto...
Transcript of Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente … · L'iPhone è un prodotto...
Facoltà di Ingegneria Corso di Studi in Ingegneria Informatica tesi di laurea
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
Anno Accademico 2007/2008
relatore Ch.mo prof. Domenico Cotroneo correlatore Ch.mo prof. Marcello Cinque candidato Diomede Mazzone matr. 041/002515
“Tutti gli esseri umani nascono liberi ed eguali in dignità e diritti. Essi sono dotati di ragione e di coscienza e devono agire gli uni verso gli altri in spirito di fratellanza.”
Dichiarazione Universale dei diritti Umani 60° anniversario 10 dicembre 1948-2008
...che sia la stella polare di ogni azione.
Indice
Introduzione 8
Capitolo 1 Sistemi operativi per dispositivi mobili 12
1.1. SISTEMA OPERATIVO SYMBIAN 13 1.2. SISTEMA OPERATIVO WINDOWS MOBILE 16 1.3. SISTEMA OPERATIVO ANDROID 17 1.4. SISTEMA OPERATIVO IPHONE OS 20
Capitolo 2 Struttura del sistema operativo iPhone OS 21
2.1. STRATIFICAZIONE 21 2.2 COCOA TOUCH 21 2.3 MEDIA 22 2.3.1 TECNOLOGIE GRAFICHE 22 2.3.2 TECNOLOGIE AUDIO 24 2.3.3 TECNOLOGIE VIDEO 25 2.4 CORE SERVICES 25 2.4.1 ADDRESS BOOK 25 2.4.2 CORE FOUNDATION 26 2.4.3 CORE LOCATION 27 2.4.4 CFNETWORK 27 2.4.5 SECURITY 28 2.4.6 SQLITE 28 2.4.7 XML SUPPORT 28 2.5 CORE OS 28 2.5.1 XNU 29 2.5.1.1 Mach 30 2.5.1.2 BSD 31 2.5.1.3 Standard I/O (I/O-Kit) 33 2.5.2 INTER PROCESS COMUNICATION (IPC) 33
2.5.2.1 Task e Thread 33 2.5.2.2 Porte 36 2.5.2.3 Messaggi 37 2.5.3 BONJOUR E SERVIZI DNS 38 2.5.4 PROTEZIONE DELLE RISORSE CONDIVISE 38
Capitolo 3 Strumenti di sviluppo per iPhone 39
3.1 INTRODUZIONE ALL'AMBIENTE COCOA 39 3.1.1 COCOA IN MAC OS X 40 3.1.2 COCOA IN IPHONE OS 44 3.2 AMBIENTE DI SVILUPPO 46 3.2.1 PIATTAFORMA SDK 47 3.2.2 XCODE 47 3.2.3 FLUSSO DI LAVORO 48 3.2.4 INTERFACE BUILDER 49 3.2.5 THE IPHONE OS SIMULATOR 53 3.2.6 PRESTAZIONI DELLE APPLICAZIONI E TOOL 53 3.2.6.1 Instruments 53 3.2.6.2 Shark 54 3.2.6.3 Altre applicazioni per le prestazioni (Mac OS X) 55 3.3 COCOA FRAMEWORK 56 3.3.1 FOUNDATION 57 3.3.1.1 Foundation: paradigmi e politiche 58 3.3.1.2 Classi Foundation 59 3.3.2 APPLICATION KIT (MAC OS X) 66 3.3.2.1 Descrizione di Application Kit 67 3.3.2.2 Classi generali per le interfacce utenti 69 3.3.2.3 Testo e caratteri 71 3.3.2.4 Grafica e colori 72 3.3.2.5 Stampe e Fax 73 3.3.2.6 Documenti e supporto al File-System 73 3.3.2.7 Internazionalizzazione e Character Input Support 73 3.3.2.8 Operating-System Services 74 3.3.2.9 Interface Builder Support 74 3.3.3 UIKIT (IPHONE OS) 74 3.3.3.1 Descrizione delle classi di UIKit 75 3.3.3.2 Application Coordination 76 3.3.3.3 Differenze di modelli nei disegni e negli eventi 76 3.3.3.4 Classi generali dell'interfaccia utente 77
3.3.3.5 Testo ed immagini 79 3.3.4 COMPARAZIONE TRA CLASSI DI APPLICATION KIT E UIKIT 79 3.4 PUBBLICAZIONE DI SOFTWARE 83 3.4.1 IPHONE DEVELOPER PROGRAM 84
Capitolo 4 Linguaggio di programmazione Objective-C 86
4.1 CENNI STORICI OBJECTIVE-C 86 4.2 PROGRAMMA DI ESEMPIO “HELLO, WORLD!” 87 4.2.1 Oggetti, Classi, metodi ed Istanze 90 4.2.2 Ereditarietà 92 4.2.3 Implementazione 92 4.2.4 Messaggi e polimorfismo 95 4.2.5 Scomporre il codice in più file 96 4.2.6 ELEMENTI COMUNI AL C 96 4.2.6.1 Tipi di dato 96 4.2.6.2 Funzioni 98 4.2.6.3 Costrutti 99 4.3 OBJECTIVE-C E OGGETTI COCOA 100 4.3.1 I VANTAGGI DI OBJECTIVE-C 101 4.3.2 DINAMICITÀ DI OBJECTIVE-C 102 4.3.3 LA CLASSE ROOT 104 4.3.3.1 NSObject 105 4.3.3.2 Classe root e Protocolli 105 4.3.3.3 Descrizione dei metodi della classe di root 106 4.3.3.4 Metodi di Classe e metodi di istanza 108 4.3.4 ALLOCAZIONE E RILASCIO DI OGGETTI 109 4.3.4.1 Memory management 110 4.3.5 ARCHIVIAZIONE DI OGGETTI 115 4.3.6 USARE OBJECTIVE 116 4.4 OBJECTIVE-C: ASPETTI AVANZATI 120 4.4.1 CATEGORIE 121 4.4.2 PROTOCOLLI 122 4.4.3 DICHIARAZIONE DELLE PROPRIETÀ 127 4.4.4 FAST ENUMERATION 130 4.4.5 SELECTOR 132 4.4.6 CREAZIONE DI OGGETTI 132 4.4.6.1 Allocazione 133 4.4.6.2 Inizializzazione 134 4.4.6.3 I metodi dealloc e finalize 144
4.4.6.4 Metodi costruttori di classe 146 4.4.7 INTROSPECTION 147 4.4.7.1 Valutazione dei rapporti gerarchici 147 4.4.7.2 Implementazione di metodi e conformità ai protocolli 149 4.4.7.3 Comparazione di oggetti 149 4.4.8 OGGETTI MODIFICABILI 150 4.4.9 RAGGRUPPAMENTO DI CLASSI 153 4.4.9.1 Struttura gerarchica 153 4.4.9.2 Creare istanze 154 4.4.9.3 Raggruppamento di classi con molteplici superclassi pubbliche 154
Capitolo 5 Presentazione di un caso di studio 157
5.1 PROGETTAZIONE 157 5.2 IMPLEMENTAZIONE 163 5.2.1 EAT&PHONEAPPDELEGATE 163 5.2.2 MAINVIEWCONTROLLER 164 5.2.3 MYCLCONTROLLER 171 5.3 SNAPSHOT DELL'APPLICAZIONE 179
Conclusioni 184
Bibliografia 188
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
8
Introduzione
Negli ultimi decenni i dispositivi mobili hanno conquistato fette di mercato sempre più
rilevanti. Risulta evidente l'incremento di funzionalità che hanno subito dalla prima
generazione fino agli ultimi modelli, che includono in sé funzionalità che fino a poco
tempo fa erano supportate da diversi dispositivi dedicati. Con lo sviluppo di hardware
sempre più sofisticato è stato possibile quindi fornire tali dispositivi di caratteristiche
anche complesse. I cellulari di ultima generazione includono infatti supporto per l'ascolto
di file musicali, riproduzione video, collegamento ad internet tramite rete Wi-Fi,
funzionalità GPS ed alcuni dispositivi anche la possibilità di accedere alla rete televisiva.
Essendo prodotti così versatili, è intuibile quanto siano diventati presenti nelle abitudini
dei consumatori. Tale processo ha influito sia nelle abitudini degli stessi sia nelle scelte
commerciali dei produttori. Con il crescere delle prestazioni hardware infatti sono
cresciuti anche gli investimenti nel software che li sfrutta. I sistemi operativi per questi
sistemi infatti devono far fronte alle carenze hardware che tali dispositivi hanno rispetto ai
sistemi tradizionali, oltre a dover gestire interfacce utente notevolmente semplificate,
considerando gli schermi di ridotte dimensioni. Aziende del settore telefonico e produttori
di dispositivi mobili hanno così elaborato alcuni approcci commerciali per venire incontro
alle esigente dei consumatori. Le prospettive future vedono l'accesso ad internet non più
come una caratteristica aggiuntiva per i cellulari, ma un requisito importante per molti
utenti. Si intuisce così l'importanza per un produttore di dispositivi mobili di intercettare
questa esigenza e trovare soluzioni ottimali per una fascia sempre più ampia di
consumatori.
Considerando il panorama commerciale a cui si fa riferimento, le proposte dei vari
produttori quindi si differenziano sia per supporti hardware che per sistemi operativo
implementati. Altra classificazione possibile nelle offerte commerciali, riguarda la libertà
concessa a sviluppatori di terze parti di distribuire software dedicato alle specifiche
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
9
piattaforme.
Nello studio che segue si è approfondito il dispositivo mobile proposto da Apple. Dal
punto di vista commerciale tale dispositivo mobile ha generato una piccola rivoluzione nel
settore, in quanto a pochi mesi dal suo lancio in quasi tutti i paesi del mondo, ha già
conquistato l'attenzione del mercato. L'11 luglio 2008 infatti la Apple ha lanciato la
seconda versione dell'iPhone, precedentemente commercializzato soltanto in alcuni Paesi.
Tale dispositivo è risultato di un tale successo che i principali dispositivi mobili, sviluppati
successivamente, vengono etichettati come “anti-iPhone”. Un caso eclatante è stato
Android, piattaforma software promossa da Google, che cerca con un approccio
commerciale antitetico di porsi come principale concorrente all'iPhone stesso. È chiaro
infatti che se un prodotto viene considerato come termine di paragone per un settore vuol
dire che dal punto di vista del mercato si è dimostrato un successo.
L'iPhone è un prodotto orientato al mercato consumer, non ha quindi un target di tipo
business. I suoi punti di forza sono quindi la facilità d'uso, l'estetica e l'usabilità nella
navigazione in internet, oltre ad avere una suite completa di programmi per l'ufficio.
L'innovazione che ha portato nel settore è sicuramente nelle sue caratteristiche hardware1,
ma sopratutto nell'usabilità, superiore a tutti i suoi predecessori. In sostanza è la sintesi di
un dispositivo hardware di elevato design, supportato da un sistema operativo semplificato
che deriva da Mac OS X, il sistema Apple per computer Desktop. iPhone OS infatti deriva
da Darwin, il kernel di Mac OS X. La struttura software inoltre ricalca abbastanza
fedelmente la struttura che è possibile trovare sui desktop di casa Apple. Le differenze tra i
due sistemi quindi, riguardano quasi esclusivamente l'interfaccia grafica e gli strumenti di
interazione con l'utente.
Il contributo che lo studio di questa tesi vuole offrire, è lo studio della piattaforma
software implementata sull'iPhone. È interessante infatti osservare il sistema operativo 1 L'iPhone è un dispositivo senza tasti reali se non uno che porta al menu generale. Tutte le sue funzioni sono quindi
accessibili tramite tecnologia multi-touch.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
10
implementato ed i framework supportati che permettono lo sviluppo di applicazioni
perfettamente coerenti con gli standard Apple e con l'usabilità che contraddistingue i suoi
software. Il sistema operativo iPhone OS è presente sia nel dispositivo mobile iPhone ma
anche nel lettore multimediale iPod Touch. Tale supporto infatti permette l'utilizzo delle
stesse applicazioni software, le limitazioni a cui va incontro riguardano esclusivamente
l'hardware. Ulteriore approfondimento offerto dallo studio che segue riguarda il
linguaggio ad oggetti Objective-C e gli strumenti di sviluppo distribuiti dalla Apple. Tale
linguaggio è infatti il privilegiato per lo sviluppo di applicazioni per sistemi Mac OS X ed
iPhone OS. Objective-C nasce dall'esigenza di estendere alla programmazione ad oggetti il
linguaggio C, è stato quindi descritto nelle sue caratteristiche principali. Objective-C nella
sua struttura è meno tipizzato di C++ ed è basato essenzialmente sullo scambio di
messaggi e su di una estrema dinamicità. In fase di esecuzione infatti Objective-C fornisce
un maggior supporto run-time alla riflessione rispetto a C++. Questa caratteristica rende
infatti Objective-C più orientato verso decisioni “dinamiche” del C++.
Il percorso seguito nello studio affrontato quindi parte dalle principali offerte commerciali
del settore e termina con lo sviluppo di una applicazione come caso di studio.
Il primo capitolo quindi riguarda il confronto delle principali offerte commerciali di
piattaforme software, proposte dalle diverse case produttrici. È interessante infatti
osservare come le diverse offerte si differenzino per il tipo di licenza d'uso utilizzata per il
sistema operativo offerto. Inoltre sono presenti differenze anche riguardo alle possibilità
che vengono date a sviluppatori di terze parti di produrre software dedicato. Tali approcci
infatti permettono di differenziare anche il target di riferimento tra gli utenti. Dare inoltre
la possibilità a sviluppatori e società esterne di creare software dedicato, apre nuovi
mercati e nuove potenzialità per il settore nonché per la piattaforma che offre tale
possibilità.
Il secondo capitolo invece si occupa nello specifico di studiare il sistema operatvo iPhone
OS. Tale studio quindi permette di relazionare il sistema implementato sul dispositivo
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
11
mobile con quello offerto sulle piattaforme desktop. Apple, come si è anticipato, non ha
infatti implementato un sistema operativo finalizzato alle dispositivi mobili, ma ha
adattato un sistema maturo come Mac OS X 10.5.
Si è affrontato nel terzo capitolo invece lo studio dei framework implementati sul
dispositivo. Attraverso tali framework è infatti possibile accedere a tutte le sue
funzionalità hardware. Apple inoltre offre agli sviluppatori strumenti che facilitano la
costruzione di software, delle sue interfacce grafiche e di strumenti che ottimizzano le
prestazioni del codice sviluppato. Insieme ai framework quindi ci si è soffermati sugli
strumenti di sviluppo, che permettono di costruire applicazioni coerenti a tutti i software
per iPhone. La coerenza ti tali strumenti permette agli utenti di avere familiarità con ogni
applicazione utilizzata. Anche in questo capitolo si è affrontato lo studio confrontando i
framework implementati in iPhone OS con quelli per Mac OS X.
Nel quarto capitolo ci si è dedicati allo studio di Objective-C. Tale linguaggio è infatti
consigliato e supportato dalla Apple per lo sviluppo di software per Mac OS X ed iPhone
OS. Objective-C è un linguaggio orientato agli oggetti. La sua principale differenza con il
C++ è il maggior supporto runtime alla riflessione. In Objective-C si può interrogare un
oggetto riguardo alle sue stesse proprietà, ad esempio se possa o meno rispondere ad un
dato messaggio, mentre in C++ ciò è impossibile a meno di fare ricorso a librerie esterne.
Nel capitolo finale si è costruito un software come caso di studio per affrontare alcune
delle tematiche descritte nei capitoli precedenti. In sostanza si sono utilizzati gli strumenti
di sviluppo forniti alla Apple, il linguaggio di programmazione ed alcune caratteristiche
hardware e software del dispositivo.
È possibile quindi, attraverso i cinque capitoli dello studio affrontato, ricostruire un quadro
completo del dispositivo mobile prodotto da Apple.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
12
Capitolo 1 Sistemi operativi per dispositivi mobili
Negli ultimi anni il mercato ha visto la diffusione di dispositivi mobili dotati di sistemi
operativi in grado di offrire servizi e funzionalità sempre più complesse. Inoltre la
diffusione dell'utilizzo di servizi internet, ha reso necessario fornire un semplice cellulare
o dispositivi più complessi della possibilità di navigare nel web. La ricerca quindi di
maggiore efficienza e funzionalità, ha spinto alcuni produttori a fornire tali dispositivi di
sistemi operativi offerti già in ambiente Desktop, oppure sviluppare soluzioni
appositamente progettate.
Un ambiente per dispositivi mobili, per sua natura, deve offrire anche funzionalità diverse
da un ambiente desktop, senza trascurare le limitazioni hardware che necessariamente
dovrà affrontare. Bisogna infatti valutare i limiti che riguardano ad esempio il display di
ridotte dimensioni, un processore di ridotte capacità elaborative, senza sottovalutare la
necessità di garantire al dispositivo una discreta autonomia elettrica. Inoltre bisogna
considerare che i sistemi operativi per dispositivi mobili potrebbero non supportare
hardware dedicato al trasferimento di dati (DMA) o per la protezione della memoria
(MMU). Un sistema desktop inoltre usufruisce di memoria virtuale che in un dispositivo
mobile risulta estremamente ridotta. Bisogna inoltre fare scelte semplificative, in quanto
un dispositivo mobile potrebbe non supportare algoritmi complessi per lo scheduling di
processi e thread implementati dai sistemi desktop. Altra difficoltà a cui vanno incontro
tali soluzioni riguardano la gestione di una possibile tastiera, fornendo eventualmente
strumenti alternativi di input con i quali interagire con l'utente.
Esistono quindi diverse soluzioni che vengono incontro a tali necessità, affrontate con
altrettanti approcci commerciali. Tra le diverse soluzioni che sintetizzeremo in questo
capitolo si potranno osservare le differenze commerciali che riguardano sia l'accesso al
codice sorgente del sistema operativo, che la possibilità di sviluppare applicazioni di terze
parti da poter istallare.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
13
Di seguito osserveremo sinteticamente le piattaforma per dispositivi mobili che si sono
distinte nel panorama commerciale, valuteremo quindi Symbian, Windows Mobile,
Android ed iPhone.
1.1. Sistema Operativo Symbian
Anche se la nascita di Symbian (Ascione, 2005) risale al 1998, bisogna considerare che il
progetto risale a molti anni prima, la prima versione infatti è datata 1989 con il nome
EPOC2. La PSION3, società entrata nella storia per aver prodotto il primo modello di
PDA4, sviluppò questo sistema operativo dalle caratteristiche innovative per quegli anni.
Era completamente multi-tasking, offriva un’interfaccia grafica ed era un’assoluta novità
se si considera che la Microsoft non aveva ancora messo in esercizio il sistema operativo
Windows.
Nel 1998 la PSION Software realizzò una fusione con Nokia, Motorola ed Ericsson per
creare Symbian con l’obiettivo di costruire un sistema operativo che potesse adattarsi a
qualunque dispositivo, dai telefoni cellulari ai palmari.
Ben presto anche altri produttori si resero conto dell’importanza del progetto e vollero
prendervi parte. L’anno seguente (1999) Matsushita, anche nota come Panasonic, acquistò
quote della società appena formata. L’ambiziosità del progetto valse a Symbian il
riconoscimento di ‘Miglior compagnia’ con il progetto a maggior potenziale nel lungo
termine.
Nel 2000 ci fu il debutto del primo modello di cellulare dotato di sistema operativo
Symbian; l’R380 prodotto dalla Ericsson. Adottava la versione 5.0 di Symbian che era
direttamente derivata da EPOC. L’anno successivo fu rilasciata la versione 6.0 che fu
installata da Nokia su tutta la serie dei Communicators 9210. Intanto il numero di società
2 EPOC deriva da Epoch ed indica la nascita di una nuova era nel campo informatico, quella dei computer tascabili. 3 PSION è l’acronimo di Potter’s Software Instrument, ossia strumentazioni scientifiche di David Potter, il fondatore
della società, a cui fu aggiunto “ON”. 4 Personal Digital Assistant
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
14
coinvolte nel progetto Symbian cresceva grazie alla partecipazione di Sony, Sanio e
Fujitsu.
Nel 2002 fu annunciata la nascita della versione 7.0 di Symbian. Il primo telefono
cellulare ad adottare tale versione fu il Nokia 6600, che nel 2003 invase i mercati.
La storia degli ultimi anni ha visto Symbian affermarsi leader nel campo dei sistemi
operativi per dispositivi mobili, la versione attualmente diffusa è la versione 9.5
annunciata nel marzo 2007.
Gli sviluppatori modificano costantemente questa struttura per integrare nuovi protocolli e
funzionalità in modo da rispondere alle esigenze delle società facenti parte del consorzio.
Come altri sistemi operativi, Symbian dispone di funzionalità di multithreading,
multitasking e protezione della memoria. Grande importanza è data all'utilizzo della
memoria mediante tecniche specifiche di Symbian che determinano la rarità degli errori
dovuti a una cattiva gestione della memoria (memory leak). Tecniche analoghe permettono
un'altrettanto efficiente gestione dello spazio su disco. Il funzionamento di Symbian è
basato su eventi e la CPU è automaticamente disabilitata quando non vi siano eventi attivi:
il corretto uso di questa tecnica aiuta ad assicurare alle batterie una durata maggiore,
caratteristica molto importante sui dispositivi mobili.
Nonostante l'origine di Symbian veda un consorzio di più società come trampolino di
lancio, bisogna notare che nel giugno del 2008 Nokia ha comunicato l'intenzione di
rilevare le quote azionarie delle altre società, al fine di divenire l'unico proprietario del
sistema operativo. Una volta completata l'acquisizione, Nokia ha comunicato l'intenzione
di renderlo open source, con la creazione di Symbian foundation. Tale fondazione, formata
dai vecchi proprietari e aperta ad altri produttori, si dovrà occupare di unificare tutte le
interfacce in una nuova release del sistema e gestirne l'apertura agli sviluppatori esterni.
Symbian non è difatti attualmente un sistema operativo open source, poiché il codice
sorgente non è disponibile pubblicamente; tuttavia, quasi tutto il codice è fornito ai
produttori di dispositivi mobili basati su Symbian e ad altri operatori. Inoltre, anche la
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
15
documentazione relativa alle API è disponibile pubblicamente, quindi chiunque ha la
possibilità di sviluppare software per Symbian.
In sostanza è possibile considerare il nuovo approccio di Nokia come la risposta
commerciale al progetto open source Android di Google, rigenerando un prodotto che
detiene già una grossa fetta di mercato delle piattaforme per dispositivi mobili. Il nuovo
progetto di Nokia infatti, si pone in una strategia di mercato non completamente open
source come Android, ma neanche completamente proprietaria come Windows Mobile di
Microsoft o iPhone OS di Apple. È infatti importante sottolineare che se è possibile
sviluppare applicazioni per Symbian, esistono solo due percorsi per installarle sui
dispositivi, attraverso pacchetti SYS oppure attraverso la preistallazione sui dispositivi da
parte di case produttrici di cellulari. Dalla versione 9.x però è stato introdotto un vincolo
per i pacchetti SYS, in quanto l'applicazione per essere installata deve essere “firmata”
(Symbian Signed) da Symbian. Con l'introduzione di tale sistema gli sviluppatori devono
pagare per utilizzare alcune delle caratteristiche più interessanti dei dispositivi mobili. Tale
vincolo ha reso Symbian meno popolare nella comunità Open Source.
Il linguaggio nativo di Symbian è il C++. Ci sono molteplici piattaforme basate su
symbian OS che forniscono SDK per sviluppatori. Singoli produttori di cellulari spesso
hanno SDK o loro estensioni direttamente scaricabili dai propri siti. Gli SDK in genere
contengono documentazione, header file e librerie necessarie per produrre software per
Symbian OS. Con la versione 9 del sistema operativo è stato aggiornato il compilatore che
include una nuova versione di GCC. Bisogna notare però che la curva di apprendimento
per sviluppare software è molto ripida. È infatti molto complicato sviluppare anche
programmi semplici e questo è dovuto al fatto che i paradigmi di programmazione sono
legati ancora a standard dei dispositivi degli anni 90. Nonostante il linguaggio nativo sia il
C è possibile sviluppare anche in OPL, Python, Visual Basic, Simkin e Perl, oltre a Java
ME e PersonalJava.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
16
1.2. Sistema Operativo Windows Mobile
Windows Mobile di Microsoft è un sistema operativo compatto, basato sulle API Win32.
In perfetto stile Microsoft tale sistema operativo è stato prodotto per diverse piattaforme
hardware, in modo da essere supportato da più dispositivi mobili, fra questi possiamo
elencare i Pocket PC, gli Smartphone ed i Portabile Media. Windows Mobile è nato nel
2003, attualmente è stata rilasciata la versione 6.1 e per il 2009 è prevista la versione 7.
La scelta commerciale di Microsoft ha privilegiato la migrazione del sistema operativo per
desktop su di un ambiente mobile. La scelta commerciale quindi ha visto prevalere la
volontà di aprire un nuovo mercato per un sistema operativo esistente e diffuso: Windows.
Un utente medio quindi utilizza sul dispositivo mobile un ambiente che generalmente
utilizza sul proprio desktop. Microsoft quindi ha scelto di alleggerire il sistema operativo
Windows delle caratteristiche più avide di risorse, senza ricorrere allo sviluppo di una
piattaforma dedicata. Nelle sue scelte commerciali inoltre ha ritenuto di lasciare il codice
del Sistema Operativo protetto da Copyright, ma permettendo a sviluppatori di terze parti
di creare software dedicato.
Il framework messo a disposizione degli sviluppatori è il Microsoft .NET Compact
Framework (.NET CF), una versione di .NET Framework ottimizzata per Windows CE.
Tale framework è una versione semplificata del più noto .NET Framework, in più dispone
di librerie costruite specificatamente per dispositivi mobili.
La versione di Windows sulla quale si basa Windows Mobile è Windows CE (Compact
Edition). Windows CE è derivato dalla famiglia Windows, ma ha un kernel differente e
non è quindi una semplice "riduzione". Le API e l'aspetto grafico sono comunque molto
simili.
Essendo Windows CE sufficientemente modulare e flessibile sono state quindi sviluppate
versioni differenti5. Tali specifiche versioni prendono il nome "commerciale" di MS
5 Bisogna considerare inoltre le differenti piattaforma hardware. Bisogna considerare infatti i processori differenti dal
x86, come MIPS, ARM e Hitach SuperH.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
17
Handheld 3.0 (e 3.1), MS Handheld 2000, Microsoft Pocket PC 2000 (e 2002), MS
Smartphone 2002, MS Windows Mobile 2003 (e 2003 Second Edition), MS Windows
Mobile 5.0 e successive. Tali varianti fanno tutte riferimento a specifiche evoluzioni della
piattaforma Windows CE, passato dalla v1.0 alla v6.1. Windows CE è un sistema
operativo ottimizzato per dispositivi con risorse limitate, ciò vuol dire ad esempio che il
suo kernel necessita, per l'avvio, di meno di un mega byte di memoria. Spesso inoltre è
configurato su dispositivi senza dischi di memoria.
1.3. Sistema Operativo Android
Nel luglio 2005 Google acquisì Android (Alfieri, 2006), una piccola compagnia di
sviluppo software con sede a Palo Alto, ed incluse in Google tutti i soci fondatori. In quel
periodo Android era ancora sconosciuta e si vociferava che Google volesse entrare nel
mercato dei dispositivi mobili. Da allora in più di un'occasione Google ha dimostrato di
essere interessata al settore, convergendo la sua ricerca verso un sistema basato su linux,
flessibile e utilizzabile su più dispositivi hardware. Nel Novembre 2007 si costituisce così
l'Open Handset Alliance (OHA). Tale Accordo commerciale include 34 compagnie con
Google come capofila, tra produttori di dispositivi mobili, società di software e soggetti
del settore delle telecomunicazioni, il cui obiettivo è sviluppare "standard aperti" per
dispositivi mobili. Android quindi è una piattaforma open source per telefoni cellulari,
basata sul sistema operativo Linux e sviluppata dall'Open Handset Alliance (OHA). Tale
proposta commerciale quindi si pone in competizione con altri soggetti del mercato, con
una strategia commerciale assolutamente differente. Nonostante ci siano stati altri casi di
sviluppo di sistemi open source per cellulari, come OpenMoko, Android è il primo caso
che vede investimenti ingenti da parte di colossi del mercato delle telecomunicazioni.
Al fine di favorire lo sviluppo di applicazioni per la piattaforma, nel gennaio 2008, Google
ha indetto un concorso a premi legato allo sviluppo di applicazioni per Android. Dal
concorso risultarono esclusi i programmatori residenti a Cuba, Iran, Siria, Nord Corea,
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
18
Sudan, Burma (Myanmar), Quebec e Italia. Le nazioni sono state escluse per adempire alla
legislazione statunitense contro il terrorismo o per impedimenti burocratici locali come nel
caso dell'Italia.
Bisogna segnalare inoltre che nell'agosto 2008 è stato presentato il primo esemplare di
dispositivo mobile con Android installato, prodotto da T-Mobile, HTC e Google. Il 22
Ottobre verrà venduto negli USA con un vincolo di contratto con T-mobile.
La piattaforma Android quindi è uno stack software per dispositivi mobili che include al
suo interno un Sistema Operativo, un Middelware ed alcune applicazioni chiave. Gli
sviluppatori possono creare applicazioni per la piattaforma usando l'apposito SDK messo a
disposizione per Android. Tali applicazioni dovranno essere scritte utilizzando il
linguaggio java e dovranno girare su Dalvik, una Virtual Machine progettata per
dispositivi mobili e che necessita a sua volta di un kernel linux.
Il 12 novembre 2007, l'OHA infatti ha rilasciato il software development kit (SDK) che
include: strumenti di sviluppo, librerie, un emulatore del dispositivo, la documentazione
(in inglese), alcuni progetti di esempio, tutorial, FAQ, e altro. Tale software di sviluppo è
installabile su qualsiasi computer x86 che usa come sistema operativo Windows XP o
Vista, Mac OS X 10.4.8 e successivi, o Linux. È anche possibile utilizzare il plug-in per
Eclipse. L'SDK è stato aggiornato alla versione 0.9 il 18 agosto 2008, questa nuova
versione è ormai la definitiva, saranno infatti minime le modifiche effettuate con il rilascio
della versione 1.0, di cui saranno dotati i primi device.
Android contiene al suo interno un insieme di applicazioni principali che ne costituiranno
il core: un client di posta e-mail, un programma per gli sms, un gestore del calendario, un
programma per la consultazione di mappe e cartine, un browser, un'agenda per i contatti e
così via. Tali applicazioni sono sviluppate in linguaggio Java.
Gli sviluppatori avranno pieno accesso allo stesso Framework ed alle stesse API utilizzate
dalle applicazioni del Core. L'architettura delle applicazioni è pensata per semplificare al
massimo il riuso dei componenti: ogni applicazione potrà rendere note le sue potenzialità e
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
19
le altre applicazioni potranno fare uso delle stesse (a patto di rispettare i vincoli sulla
sicurezza imposti dal framework). Tale meccanismo offre un modo estremamente
semplice per sostituire o aggiornare componenti da parte dell'utente.
Per sviluppare le applicazioni esistono un vasto insieme di servizi e sistemi tra i quali una
ricca gamma di controlli, un provider dei contenuti che rende le applicazioni capaci di
accedere contenuti di altre applicazioni, un gestore delle risorse, un gestore delle notifiche
ed un gestore delle attività. Tali strumenti permettono così di sviluppare agevolmente
anche applicazioni complesse ed allo stesso tempo permetterà un facile aggiornamento
delle stesse con la sostituzione di singoli componenti elementari. Android include anche
un vasto insieme di librerie scritte in C/C++ utilizzate da vari componenti del sistema
stesso. Queste potenzialità sono disponibili agli sviluppatori attraverso il Framework
applicativo di Android. Tra le principali librerie troviamo anche Librerie di sistemi C
ottimizzate per dispositivi mobili basati su linux, Librerie multimediali, un gestore delle
interfacce, una libreria WebCore, SGL (per lo sviluppo 2D), librerie 3D ed SQLite, come
Data Base da utilizzare alla base delle applicazioni. Android include anche un set di
librerie del Core che forniscono la maggior parte delle funzionalità disponibili nelle
librerie di sistema del linguaggio di programmazione Java standard. Tutti i software
sviluppati, a differenza di altre piattaforme, non sono comunque posti a nessun vincolo per
la loro installazione.
È da notare comunque il carattere innovativo di tale progetto in quanto dal 21 ottobre 2008
la piattaforma è reperibile come Android Open Source Project. Bisogna comunque notare
che alcune parti di Android che si riferiscono a specifico hardware non sono rilasciate
come open source e non vengono, comunque, considerate parte integrante del progetto.
Altre limitazioni alla licenza fanno emergere dubbi su quanto Google voglia gestire in stile
open source tale piattaforma, inoltre c'è da dire che attraverso la licenza Apache è data la
possibilità a produttori di terze parti di sviluppare estensioni software senza dover
sottoscrivere una licenza open source. Tale approccio, secondo molti esperti del settore,
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
20
genererà un proliferare di licenze alternative. Google comunque ha dichiarato in più di
un'occasione a voler mantenere il progetto Open Source.
Si devono comunque segnalare altri progetti che vedono linux come sistema operativo per
dispositivi mobili. Uno di questi è Maemo, basato sulla distribuzione linux Debian. Tale
sistema è utilizzato da Nokia per alcuni dispositivi portatili touch screen.
1.4. Sistema operativo iPhone OS
Apple inc. ha fatto il suo ingresso nei dispositivi mobili nel 2007 con una prima versione
dell'iPhone, successivamente nel luglio del 2008 è stata presentata una seconda versione
disponibile in molti più Paesi.
Come tutti gli altri prodotti Apple, anche con l'iPhone, la casa produttrice ha deciso di
legare il sistema operativo ad un unico dispositivo hardware. A differenza quindi dei suoi
concorrenti non ha previsto la portabilità del suo sistema su altri dispositivi. Il sistema
operativo in oggetto è inoltre l'apertura nel settore mobile del sistema operativo già
utilizzato in ambiente desktop.
La descrizione dettagliata di tale sistema verrà offerta nei capitoli successivi. L'unica
caratteristica degna di nota in questo frangente è il ritorno ad un ambiente chiuso di
sviluppo del sistema operativo in cui la casa produttrice detiene tutti i diritti. Inoltre Apple
ha rilasciato un kit di sviluppo per applicazioni di terze parti, ma prima di essere installate
sui dispositivi necessitano di un'approvazione della Apple stessa. È possibile quindi
sintetizzare l'approccio commerciale di Apple come in antitesi con il diretto concorrente
Android.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
21
Capitolo 2 Struttura del sistema operativo iPhone OS
2.1. Stratificazione
iPhone OS (Apple, The Cocoa Environment) è costituito da 4 strati. Cocoa Touch, Media,
Core Services e Core OS (Figura 1). Gli strati più bassi del sistema sono i servizi
fondamentali su cui si basano tutte le applicazioni, mentre gli strati di livello superiore
contengono tecnologie e servizi più sofisticati.
Gli strati più alti forniscono astrazioni dei costrutti dei livelli inferiori, facilitando lo
sviluppo di software. L'accesso ai livelli più bassi è comunque garantito agli sviluppatori
che ne vogliano sfruttare tutte le potenzialità.
2.2 Cocoa Touch
Lo strato Cocoa Touch comprende i framework UIKit e foundation (UIKit.framework e
Foundation.framework), che forniscono gli strumenti di base e le infrastrutture necessarie
per realizzare la grafica e le applicazioni in iPhone OS. Cocoa Touch comprende anche
altri servizi chiave per accedere alle caratteristiche del dispositivo, ad esempio i contatti
dell'utente.
UIKit (UIKit.framework) è un framework Objective-C (Apple, The Objective-C 2.0
Programming Language, 2006) necessario per la realizzazione delle infrastrutture grafiche
Figura 1: Stratificazione iPhone OS
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
22
nelle applicazioni in iPhone OS.
Ogni applicazione utilizza Cocoa Touch per l'implementazione delle seguenti
caratteristiche:
• Application Management
• Supporto alla Grafica
• Supporto alla gestione di eventi
• Gestione dell'interfaccia utente
• Oggetti che rappresentano le interfacce ed i controlli standard del sistema
• Supporto per testo e contenuti web
o Oltre a fornire il codice fondamentale per costruire le applicazioni, UIKit incorpora
anche il supporto per alcune funzioni specifiche del dispositivo come quelle elencate
di seguito:
• Accelerometro dati
• La fotocamera integrata
• La libreria fotografica dell'utente
• Device-specific information
2.3 Media
Lo strato Media provvede a fornire strumenti per la gestione della grafica, dell'audio e
delle tecnologie interne, volti a velocizzare lo sviluppo di applicazioni che sfruttano le
caratteristiche hardware del dispositivo. I framework degli strati alti rendono facile la
creazione di animazioni grafiche ed i livelli inferiori del framework danno accesso agli
strumenti necessari per personalizzare al meglio il software da implementare.
Di seguito verranno descritte le tecnologie incluse nello strato Media, che a loro volta
includono set di librerie.
2.3.1 Tecnologie Grafiche
La grafica è una caratteristica importante per tutte le applicazioni iPhone. Ogni qualvolta
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
23
sia possibile, lo sviluppatore può sfruttare immagini, View e funzioni fornite dal
framework UIKit per migliorare l'interfaccia utente dell'applicazione sviluppata. Se tali
strumenti forniti da UIKit dovessero risultare insufficienti è sempre possibile accedere alle
tecnologie descritte di seguito. Le seguenti librerie infatti sono strumenti molto utili per
gestire ogni aspetto grafico 2D e 3D delle applicazioni.
OpenGLES.framework
Il framework OpenGL ES si basa sulle specifiche OpenGL ES v1.1 e fornisce strumenti
per il disegno di contenuti 2D e 3D. È un framework scritto in C che lavora a stretto
contatto con l'hardware per fornire un elevato frame rate, fondamentale nello sviluppo di
giochi.
OpenGLES.framework è sempre usato in congiunzione con le interfacce EAGL. Tali
interfacce sono parte del framework OpenGL ES e fanno da interfaccia tra il codice dei
disegni OpenGL ES e le finestre dell'applicazione da sviluppare.
QuartzCore.framework
Il framework Quartz Core contiene le interfacce Core Animation. Core Animation è
un'avanzata tecnologia di animazione che utilizza un path di rendering ottimizzato per
attuare complesse animazioni ed effetti visivi.
Fornisce un' interfaccia di alto livello objective-C per la configurazioni di animazioni ed
effetti. Successivamente il rendering è realizzato in hardware per migliorare le prestazioni.
Core Animation è integrato in più parti del Sistema Operativo iPhone OS includendo
alcune classi UIKit come UIView e fornendo animazioni per diversi effetti grafici classici
del sistema operativo. E' possibile utilizzare interfacce Objective-C di questo framework
per creare animazioni personalizzate.
CoreGraphics.framework
Il Framework Core Graphics (CoreGraphics.framework) contiene le interfacce per il
disegno Quartz 2D. Quartz è lo stesso framework utilizzato per il disegno in Mac OS X.
Fornisce il supporto per il disegno base, antialiasing, rendering, gradienti, immagini,
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
24
colori, trasformazioni spaziali, creazione di pdf, visualizzazione ed analisi. Anche se l'API
è basato su C, utilizza oggetti basati su astrazioni per rappresentare gli oggetti
fondamentali dei disegni, semplificando il riutilizzo dei contenuti grafici.
2.3.2 Tecnologie Audio
Le tecnologie audio offerte in iPhone OS includono la capacità di riprodurre e registrare
audio di alta qualità nonché di gestire le funzionalità di vibrazione del dispositivo. Le
tecnologie audio supportano i seguenti formati:
• AAC
• Apple Lossless (ALAC)
• A-law
• IMA/ADPCM (IMA4)
• Linear PCM
• µ-law
Core Audio
Core Audio è un'interfaccia di base che supporta la manipolazione di audio multicanale, è
possibile quindi generare, registrare, mescolare e riprodurre audio. È inoltre possibile
utilizzare Core Audio per accedere alle funzionalità di vibrazione.
Sono previsti i seguenti framework:
CoreAudio.framework
Fornisce informazioni sulle caratteristiche audio e sul formato di file.
AudioToolbox.framework
Fornisce servizi di riproduzione e registrazione per file e flussi audio. Questo framework
prevede anche il supporto per la gestione di file audio e riproduzione di suoni ed allarmi di
sistema.
AudioUnit.framework
Fornisce servizi per l'utilizzo di unità audio e moduli di elaborazione.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
25
OpenAL
Oltre a Core Audio, iPhone OS include il supporto per l'audio Open Library (OpenAL).
L'interfaccia OpenAL è uno standard multipiattaforma per la gestione 3D audio nelle
applicazioni. È possibile quindi utilizzare questa libreria in tutte le applicazioni che, come
i giochi, richiedono caratteristiche audio di alta qualità e di gestione 3D dello stesso.
2.3.3 Tecnologie Video
iPhone OS fornisce supporto per i video full screen attraverso il framework Media Player
(MediaPlayer.framework). Questo framework supporta la riproduzione di file video con
estensione .mov .mp4 .m4v .3gp ed usa le compressioni standard H.264, MPEG-4 Part 2
video e numerosi formati audio, tra cui: AAC, Apple Lossless (ALAC), µ-law, linear
PCM, IMA/ADPCM (IMA4), A-law.
2.4 Core Services
Il Core services fornisce i servizi di sistema necessari a tutte le applicazioni. Anche non
utilizzando tale strato in maniera diretta, ogni altra tecnologia nel sistema si basa su di
esso.
Core Services quindi fornisce i seguenti servizi:
• Address Book
• Core Foundation
• Core Location
• CFNetwork
• Security
• SQLite
• XML Support
2.4.1 Address Book
Il framework Address Book (AddressBook.framework) fornisce un accesso ai contatti
salvati sul dispositivo. Le applicazioni che hanno bisogno di queste informazioni, come
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
26
programmi di mail o di chat, possono usare questo framework per accedere direttamente ai
dati dei contatti salvati, tali programmi possono usare i dati sia internamente che
visualizzarli in un'interfaccia grafica.
Il framework Address Book UI (AddressBookUI.framework) completa
AddressBook.framework fornendo un'interfaccia grafica per accedere ai contatti utente,
utilizzando classi Objective-C di questo framework è possibile usare le interfacce standard
del sistema per presentare i contatti e crearne di nuovi.
2.4.2 Core Foundation
Il framework Core Foundation (CoreFoundation.framework) è un set di interfacce che
forniscono gestione dei dati di base e dei servizi per le applicazioni in iPhone.
Questo framework fornisce supporto per:
• Tipi di dato Collection (arrays, sets, e così via)
• Bundle
• Gestione di stringhe
• Gestione delle date e del tempo
• Gestione di blocchi di dati grezzi
• Gestione delle preferenze
• Manipolazione di stream e di URL
• Supporto a Thread ed all'avvio del Loop
• Comunicazione di porte e Socket
Il framework Core Foundation è strettamente collegato al framework Foundation, che
fornisce interfacce Objective-C per le stesse caratteristiche di base. In situazioni dove si
necessita mischiare oggetti Foundation e tipi Core Foundation è possibile utilizzare un
ponte “toll-free” che esiste tra i due framework. Per “toll-free” si intende la possibilità di
usare tipi Core Foundation e Foundation in maniera intercambiabile nei metodi e nelle
funzioni dei rispettivi framework. Questa caratteristica è abilitata per molti tipi di dati,
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
27
incluse collection e tipi di dato stringa. La descrizione di tipi e classi per ogni framework,
indica se un oggetto è toll-free e, nel caso, il tipo di oggetto e con chi è collegato.
2.4.3 Core Location
Il framework Core Location (CoreLocation.framework), permette di stabilire la corretta
latitudine e longitudine del dispositivo, il framework infatti utilizza le caratteristiche
hardware per determinare la posizione geografica del dispositivo, attraverso il GPS, la
triangolazione delle celle o le informazioni del segnale WiFi. La precisione con cui
elabora la locazione è in funzione della tecnologia che utlizza per ricavarla. L'applicazione
Maps utilizza questa caratteristica per visualizzare in una mappa la posizione corrente
dell'utente, è così possibile integrare questa tecnologia in qualunque applicazione necessita
di informazioni sulla posizione geografica. Un esempio possibile è l'individuazione di
servizi, locali o ristoranti nelle vicinanze della posizione localizzata.
2.4.4 CFNetwork
Il framework CFNetwork (CFNetwork.framework) è un set di interfacce che fornisce
astrazioni orientate ad oggetti per gestire i protocolli di networking. Tali astrazioni
forniscono controllo dettagliato sul protocollo dello stack e facilitano l'utilizzo di costrutti
di basso livello come i socket BSD. Inoltre questo framework facilita compiti come la
comunicazione via FTP e server HTTP e risolve host DNS.
Di seguito sono elencati alcuni compiti facilitati dal framework CFNetwork:
• Uso di Socket BSD
• Creare connessioni protette SSL o TLS
• DNS hosts
• Lavorare con server HTTP e HTTPS
• Lavorare con server FTP
• Pubblicare, risolvere, e cercare servizi Bonjour
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
28
2.4.5 Security
In aggiunta alle caratteristiche integrate di sicurezza, iPhone OS fornisce un framework
(Security.framework) esplicitamente dedicato agli sviluppatori che vogliano aumentare la
sicurezza nella gestione dei dati nelle applicazioni. Tale framework fornisce interfacce per
gestire certificati, a chiave pubblica e privata e politiche di sicurezza, supporta infatti la
generazione pseudo casuale di numeri crittograficamente sicuri. Inoltre fornisce supporto
al salvataggio di certificati e chiavi crittografate nel portachiavi (keychen), un luogo sicuro
per salvare dati sensibili degli utenti.
Le interfacce CommonCrypto forniscono supporto addizionale per codifica simmetrica,
HMAC e Digests. Le caratteristiche Digests supportano funzioni compatibili con le
funzionalità normalmente trovate nelle librerie OpenSSL, non disponibili in iPhone OS.
2.4.6 SQLite
La libreria SQLite fornisce la possibilità di integrare nell'applicazione un piccolo database
SQL, senza dover accedere in remoto ad un altro database. Nell'applicazione quindi è
possibile creare un file database locale e gestire tabelle e record direttamente su questo
file. La libreria è costruita per un uso generico, ma è ottimizzato per fornire un accesso più
rapido ai record del database.
2.4.7 XML Support
Il framework Foundation fornisce la classe NSXMLParser per recuperare elementi da un
documento XML. In aggiunta, il supporto alla manipolazione di contenuti XML è fornito
dalla libreria libXML2. Questa libreria open source permette di analizzare o scrivere dati
XML in modo rapido e trasformare facilmente il contenuto XML in HTML.
2.5 Core OS
iPhone OS, nel suo strato Core OS, fornisce un insieme di interfacce per l'accesso a molte
funzionalità di basso livello del sistema operativo. La richiesta di accessi a queste
funzionalità avviene attraverso la libreria LibSystem. Le interfacce forniscono il supporto
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
29
alle seguenti funzionalità:
• Threading (POSIX threads)
• Networking (BSD sockets)
• Accesso al File-system
• Standard I/O
• Bonjour e servizi DNS
• Locale information
• Allocazione della Memoria
• Math computations
Lo strato Core OS comprende il kernel, driver e l'interfaccia di base del sistema operativo.
La struttura del kernel è ereditata da Mac OS X che a sua volta è basato sul progetto
Darwin, in particolare la versione iPhone OS 1.0 è basata su Darwin 9.0.0d1.
Specificatamente per iPhone ed iPod touch il sistema operativo è stato ottimizzato per
CPU con architettura ARM.
2.5.1 XNU
Darwin è costruito su XNU, un kernel che può essere visto in maniera semplificata come
composto da un core basato su Mach (Cesare, 2007) 3, una “personalità” da sistema
operativo basata su FreeBSD 5 ed un ambiente runtime orientato agli oggetti per le
estensioni del kernel (driver compressi).
È da notate che un kernel in esecuzione contiene numerosi driver che non risiedono nel
codice base del kernel, ma hanno i propri package Darwin. Da questo punto di vista è
possibile affermare che il kernel di Mac OS X è qualcosa di più di XNU anche se al
termine XNU si fa riferimento al codice di base più tutte le estensioni del kernel stesso.
Fatta questa premessa possiamo considerare le seguenti parti principali del kernel di Mac
OS X:
• Mach – lo strato di servizio
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
30
• BSD (Cesare, 2007) – il fornitore primario dell'interfaccia di programmazione di
sistema
• L'I/O Kit (Cesare, 2007) – l'ambiente di runtime per i driver
2.5.1.1 Mach
Se il kernel XNU è il cuore del Mac OS X, allora il Mach può essere considerato il cuore
di XNU. Il Mach fornisce servizi di basso livello critici che sono trasparenti alle
applicazioni. Gli aspetti del sistema di cui è responsabile il Mach includono:
• Astrazione hardware (in parte)
• Gestione del processore (incluso lo scheduling ed il multiprocessing simmetrico)
• Preempitive multitasking (incluso i supporto ai task ed ai thread)
• Gestione della memoria virtuale (incluso il paging di basso livello, la protezione della
memoria, la condivisione e l'ereditarietà)
• Meccanismi IPC di basso livello (che sono la base dello scambio di messaggi nel
kernel)
• Supporto real-time (che permette alle applicazioni time-sensitive di avere accesso
latency-bounded alle risorse del processore)
• Supporto al debugging del kernel6
• Console I/O
Mach inoltre gestisce nativamente la presenza di più architetture hardware come i
processori PowerPC o i processori x86, nonostante Apple abbia annunciato la transizione
di Mac OS X su piattaforme x86 solo nel 2005.
Per sua costruzione il Mach è identificato come un sistema microkernel7. Xnu però non lo
6 Il kernel Debugger di basso livello integrato nello XNU si chiama KDB (o DDB). È implementato nella porzione
Mach del Kernel, così come KDP che è il protocollo remoto di debugging del Kernel usato dal debugger GNU
(GDB). 7 Fu realmente un microkernel solo dalla versione 3. Nelle versioni precedenti aveva un'implementazione monolitica
in cui BSD e Mach occupavano lo stesso spazio di indirizzi.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
31
utilizza come un microkernel tradizionale, nonostante l'implementazione utilizzata dalla
Apple derivi dalla versione 3. Vari sottosistemi che sarebbero implementati come server
nello user-space di un vero sistema microkernel, sono parti proprie del kernel nel Mac OS
X. In particolare la porzione BSD dello XNU, l'I/O Kit ed il Mach risiedono nello stesso
spazio degli indirizzi: tuttavia hanno responsabilità ben definite che li separano in termini
di funzioni ed implementazioni.
2.5.1.2 BSD
Il kernel XNU contiene una sostanziale quantità di codice derivante da BSD, tuttavia se
alcune porzioni sono sostanzialmente simili alle originali, molte altre sono abbastanza
differenti per permettere l'interazione con entità estranee a BSD, come ad esempio l'I/O
Kit ed il Mach.
Le funzioni di cui BSD è responsabile sono:
• Il modello dei processi nello stile BSD
• I segnali
• Gli user Ids, i permessi e le politiche base della sicurezza
• Le APIs del POSIX
• Le APIs di I/O asincrono
• Le chiamate di sistema nello stile BSD
• Lo stack TCP/IP, i socket BSD ed il firewalling
• Le Network Kernel Extensions (NKEs)8
• Lo strato VFS (Virtual File System) e numerosi file system, incluso un meccanismo di
journaling a livello VFS indipendente dal file system.
• I meccanismi IPC del System V e del POSIX
• Una struttura crittografica intra-kernel
8 Si tratta di un tipo particolare di estensioni del kernel che rendonol'architettura networking di BSD pienamente
compatibile con XNU.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
32
• Un sistema di notifiche (basato sul meccanismo kqueque/kevent del FreeBSD), un
servizio a livello di sistema che abilita notifiche tra applicazioni e dal kernel alle
applicazioni
• Il meccanismo di notifica del cambio di file system fsevent (utilizzato in Mac OS X
dalla tecnologia di ricerca Spotlight)
• Le liste di controllo degli accessi e la struttura d'autorizzazioni kauth, una struttura
estensibile di autorizzazioni a carattere generale.
Alcune funzionalità del kernel hanno implementazioni a basso livello in una porzione del
kernel con strati d'astrazione d'alto livello in un'altra porzione. Per esempio la tradizionale
struttura dei processi (struct proc), che è la struttura dati primaria del kernel rappresenta
un processo UNIX, è contenuta nella porzione BSD come la u-area9.
Nel Mac OS X non è eseguito nessun processo BSD: esso coincide con un task Mach che
contiene uno o più thread Mach e sono questi thread ad essere eseguiti. Consideriamo ad
esempio la chiamata di sistema fork() che, a meno di varianti come vfork(), è il solo modo
per creare un nuovo processo in un sistema UNIX. In Mac OS X i task ed i thread son
creati e manipolati attraverso delle “Mach calls”, che l'utente non utilizza direttamente:
l'implementazione BSD-style della fork utilizza queste chiamate per creare un task ed un
thread. Dal punto di vista del chiamante la fork(), questa appare atomica e con le strutture
dati Mach e BSD-style sincronizzate.
Similmente la UBC (Unified Buffer Cache) del BSD si aggancia al sottosistema della
memoria virtuale del Mach. La UBC permette al file system ed al sottosistema della
memoria virtuale di condividere i buffer di memoria del kernel. Ogni memoria virtuale di
un processo contiene in genere una mappa sia della memoria fisica che dei file su disco.
Unificando la buffer cache si ottiene un singolo”deposito” per varie entità, riducendo il
numero di accessi al disco e la quantità di memoria utilizzata.
9 È il nome di strutture contenenti dati per-process o per -thread che è possibile scambiare.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
33
2.5.1.3 Standard I/O (I/O-Kit)
Lo XNU ha un framework orientato agli oggetti per i driver di periferiche chiamato
I/OKit. Tale framework utilizza un ristretto sottogruppo del C++10 come linguaggio di
programmazione. L'implementazione di I/O Kit consiste nelle librerie C++ residenti nel
kernel ed un framework per lo spazio utenti (IOKit.framework). Il framework dello spazio
utente quindi è utilizzato per scrivere programmi user-space che comunicano con l'I/O Kit.
Inoltre l'obbiettivo di utilizzare un framework orientato agli oggetti per i driver di
periferica, è permettere agli sviluppatori di concentrarsi sullo sviluppo di determinate
caratteristiche, senza dover implementare anche quelle comuni a tutte le periferiche.
Bisogna notare inoltre che permette il caricamento dinamico dei driver, gestisce le
periferiche hot-plug ed autoconfigurabili. La maggioranza dei driver sono scritti per essere
eseguiti nello spazio utente in modo da migliorare la sicurezza del sistema in quanto il
fallimento di un driver eseguito nello spazio utente non è in grado di bloccare il kernel.
L'architettura runtime dell'I/O Kit è modulare ed a strati. Fornisce un'infrastruttura per
catturare, rappresentare e mantenere relazioni tra varie componenti hardware e software
che sono coinvolte nelle connessioni I/O. In questo modo l'I/O Kit presenta delle
astrazioni dell'hardware sottostante al resto del sistema.
2.5.2 Inter Process Comunication (IPC)
Il Kernel Mach consente la creazione e la soppressione di più task, che sono simili ai
processi ma hanno più thread di controllo. La maggior parte delle comunicazioni si
compie per mezzo di messaggi (IPC), i messaggi si inviano e si ricevono attraverso porte.
Nel seguito descriveremo le caratteristiche dei thread, task, porte e messaggi.
2.5.2.1 Task e Thread
Il Mach divide le astrazioni tradizionali dei processi UNIX in due parti: un task ed un
10 Tale sottoinsieme non include caratteristiche che Apple ritiene siano rischiose in un kernel multithread (eccezioni,
ereditarietà multipla, i modelli, run-time type information).
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
34
thread. Nel Mac OS X, i termini “thread” e “process” hanno delle connotazioni specifiche
a seconda del contesto ovvero dell'ambiente user-space. All'interno del kernel, un processo
BSD (analogo ad un tradizionale processo UNIX), è una struttura dati con una
corrispondenza 1-a-1 verso un task Mach.
Un task del Mach ha le seguenti caratteristiche fondamentali:
• È un ambiente d'esecuzione ed un'entità statica. Ciò vuol dire che non esegue
operazioni, ma fornisce una struttura tale da permettere ad altre entità (thread) di
eseguire operazioni.
• È l'unità base dell'allocazione delle risorse. Un task contiene quindi una collezione di
risorse come accesso ai processori, spazio di indirizzi virtuali paginato, spazio IPC,
gestori delle eccezioni, credenziali, descrittori di file, stato della protezione, stato della
gestione dei segnali e statistiche. Notiamo inoltre che le risorse del task includono
oggetti UNIX, che in Mac OS X sono contenuti in un task mediante la sua
corrispondenza 1-a-1 con la struttura del processo BSD.
• Rappresenta il contorno di protezione di un programma. Un task non può accedere alle
risorse di un altro task senza aver ottenuto una esplicita autorizzazione.
• Un thread è l'attuale entità di esecuzione nel Mach ed è un punto di controllo di flusso
all'interno di un task. Ha le seguenti caratteristiche:
• Esegue all'interno di un task, rappresentando un program counter indipendente nel task.
Un thread è anche l'unità schedabile fondamentale, ogni thread quindi è schedato pre-
ventivamente ed indipendentemente sia dai thread dello stesso task che da quelli di altri
task.
• Il codice eseguito da un thread risiede nello spazio degli indirizzi del proprio task.
• Ogni task può contenere nessuno o più thread, ma ogni thread deve riferirsi ad un unico
task. Si noti comunque che un task senza thread, pur esistendo non può andare in
esecuzione.
• Tutti i thread di un task condividono le risorse del task stesso. Ciò vuol dire che
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
35
condividendo anche la memoria, è possibile che un thread sovrascriva la memoria di un
altro thread, senza per altro dover ottenere privilegi aggiuntivi. Poiché ci possono
essere numerosi thread all'interno dello stesso task, tali thread devono cooperare.
• È possibile che un thread abbia il proprio gestore delle eccezioni.
• Ogni thread ha un proprio stato di computazione, quindi i registri di processore, un
program counter ed uno stack. Poiché abbiamo detto che le risorse sono condivise tra i
thread dello stesso task, ogni thread può accedere agli stack degli altri thread dello
stesso task.
• Per amministrare le chiamate di sistema un thread utilizza uno stack del kernel di 16k
di dimensione.
È possibile quindi riassumere un task come passivo, con risorse proprie e come unità base
della protezione. Invece è possibile definire un thread come attivo, che esegue istruzioni
ed è l'unità base del controllo di flusso. Da tutte queste considerazioni è intuibile che
creare o distruggere un task è notevolmente più costoso che creare e distruggere un thread.
Al contrario dei processi UNIX, non si mantiene una relazione tra un task Mach ed il suo
task creatore. Relazione che ovviamente esiste tra il thread ed il suo task contenitore. Ad
ogni modo il kernel mantiene le relazioni padre-figlio a livello di processo nelle strutture
BSD. Tuttavia è anche possibile considerare una relazione padre-figlio anche per i task, in
quanto un task generato eredita alcuni aspetti del “task padre”, come le porte registrate, le
porte eccezioni e botstrap, token di verifica e sicurezza, regioni di mapping condivise e set
di processori. Notiamo inoltre che se il task padre è marcato come inattivo, il figlio è
assegnato al processor set di default.
Una volta creato un task, chiunque con un valido identificatore di task (e gli appropriati
diritti ad una porta IPC Mach) può compiere operazioni sul task. Un task può inoltre
spedire il proprio identificatore ad un altro task in un messaggio IPC, se lo desidera.
Il kernel utilizza questa struttura di task e thread per dividere le proprie funzionalità in vari
flussi di lavoro, utilizzando un singolo task (il kernel task) con thread multipli per svolgere
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
36
le operazioni del kernel stesso. È possibile quindi descrivere XNU come un kernel
monolitico, contenente componenti marcatamente differenti (Mac, BSD, I/O Kit) che
girano tutti come gruppi di thread all'interno di uno stesso task, condividendo lo stesso
spazio di indirizzi.
2.5.2.2 Porte
Una porta Mach è un'astrazione con varie sfaccettature implementata come una coda a
lunghezza finita di messaggi. Oltre alle porte Mach, il Mac OS X fornisce altri
meccanismi IPC, sia nello spazio kernel che in quello utente. Possiamo elencare ad
esempio le IPC del Posix e del System V, il descriptor passing, le notifiche multiple e gli
Apple Events.
Ogni porta ha dei diritti associati (right). Tali diritti sono gestiti dal kernel e devono essere
posseduti dal task che voglia gestire tale porta. I right determinano quali task possono
inviare messaggi ad una porta e quale task può ricevere i messaggi attribuiti alla porta. È
da notare comunque che mentre è possibile per più task inviare messaggi ad una stessa
porta, soltanto uno più ricevere messaggi da essa.
Nell'approccio orientato agli oggetti, una porta è un riferimento ad un oggetto, svariate
astrazioni del Mach quindi sono rappresentate da porte. In tal senso una porta funziona
come un provider di accesso protetto ad una risorsa di sistema. Ogni task o thread quindi è
accessibile tramite porte, task port o thread port. Ognuno di questi accessi richiede una
capacità (capability) della porta, vale a dire il diritto di mandare o ricevere messaggi da
quella porta e quindi l'oggetto che la porta rappresenta. Ogni oggetto può avere più porte
da cui ricevere messaggi per differenziare livelli di accesso o funzionalità. L'oggetto che
detiene i diritti di ricezione da una porta può quindi ricevere il messaggio, processarlo ed
eventualmente effettuare un'operazione richiesta nel messaggio. Ogni task, come ogni
thread al suo interno, ha un exception port. Ad esempio un gestore degli errori può
registrare una delle sue porte come un exception port di thread. Quando avviene
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
37
un'eccezione, un messaggio può essere inviato a tale porta ed il gestore può riceverlo e
processarlo.
Poiché tutti i thread hanno in comune il task, questi possono accedere a tutte le porte del
task stesso. Allo stesso tempo, tramite il passaggio di diritti attraverso messaggi IPC, è
possibile che un task dia la possibilità ad un altro task di accedere alle proprie porte. Le
porte inoltre possono essere raggruppate in un port set. Pur condividendo un ricevitore e la
coda di messaggi, è possibile inviare il messaggio alla singola porta del set.
Notiamo inoltre che per un sistema Mach, le porte sono progettate per essere trasparenti
alla rete, permettendo ai task di comunicare tra le macchine della rete senza preoccuparsi
di sapere la collocazione delle porte stesse. Tale meccanismo è garantito da un server
centrale per lo scambio di messaggi. Tale approccio ad un sistema di IPC distribuito però
non è implementato in Mac OS X, che lo rende comunque possibile attraverso meccanismi
di alto livello come l'API Distribuited Objects del Cocoa.
Notiamo inoltre che una porta può essere utilizzata solo per mandare messaggi in un'unica
direzione, quindi non rappresenta l'end point di una comunicazione bidirezionale, al
contrario del Socket BSD.
2.5.2.3 Messaggi
I messaggi IPC del Mach sono data object che i thread si scambiano per comunicare.
Tramite messaggi quindi avviene anche la comunicazione tra il kernel ed i task utente. Un
messaggio inoltre può contenere dati in-line oppure puntatori a dati out-of-line (OOL). Da
notare è che il trasferimento di dati OOL è una ottimizzazione per i grandi trasferimenti, il
kernel infatti alloca una regione di memoria per il messaggio nello spazio degli indirizzi
del ricevente, senza creare una copia fisica del messaggio. Un messaggio può contenere
dati arbitrari di programma, copia di blocchi di memoria, eccezioni, notifiche, capacità di
porta e così via. In particolare l'unico modo per trasferire le capacità di porta da un task ad
un altro è attraverso messaggi.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
38
I messaggi Mach sono trasferiti in modo asincrono. Anche se un solo task può usufruire
dei diritti di ricezione di una porta, molti threads in un task possono cercare di ricevere
messaggi su una porta, in questo caso solo uno dei threads avrà successo nella ricezione.
2.5.3 Bonjour e servizi DNS
Bonjour è un marchio registrato usato da Apple per denominare la sua implementazione
del protocollo Zeroconf dell'IETF, una tecnologia basata sulle reti locali introdotta dalla
versione 10.2 di Mac OS X. Questa tecnologia usa il pacchetto standard DNS in un modo
nuovo, sviluppa cioè dei nuovi servizi utilizzando una tecnologia relativamente vecchia
come quella del DNS over IP. Inizialmente la tecnologia sviluppata da Apple utilizzava il
nome Rendezvous ma, per motivi legali, con la versione 10.4 del suo sistema operativo
Apple ha rinominato la tecnologia con il nome di Bonjour.
2.5.4 Protezione delle risorse condivise
Per consentire un funzionamento sicuro su macchine multiprocessore l'accesso alle risorse
condivise (file, strutture dati, ecc) sono serializzati in modo da impedire ai processi di
modificare la stessa risorsa nello stesso tempo. Le operazioni atomiche, spinlock, sezioni
critiche, sistemi a mutua esclusione e gestione serializzata sono i possibili metodi per
impedire accessi pericolosi a strutture condivise. Inoltre, come Linux e FreeBSD, Darwin
implementa sistemi a mutua esclusione (mutex) a grana fine per ottenere elevate
prestazioni nei sistemi multiprocessore.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
39
Capitolo 3 Strumenti di sviluppo per iPhone
3.1 Introduzione all'ambiente Cocoa
Cocoa è un framework object-oriented che fornisce un ambiente runtime per applicazioni
eseguibili in Mac OS X e per sistemi che utilizzano tecnologie multi-touch, come l'iPhone
ed iPode touch. Cocoa quindi fornisce supporto agli sviluppatori dalla fase di design
all'implementazione dell'applicazione stessa.
Xcode (Apple, The development Environment) è invece un ambiente integrato di sviluppo
software che permette di sviluppare applicazioni sia per Mac OS X che per iPhone OS, la
combinazione di questo ambiente di sviluppo e di Cocoa facilita lo sviluppo di
applicazioni molto articolate.
Come tutti gli ambienti di sviluppo, Cocoa è composto da due parti: ambiente di runtime
ed ambiente di sviluppo. Nell'ambiente di runtime le applicazioni hanno un'interfaccia
utente ben integrata con le altre parti del sistema operativo per il quale vengono
sviluppate, ad esempio risultano, in Mac OS X, ben integrate con il Finder, il Dock e le
altre peculiarità del sistema stesso.
L'aspetto più interessante di Cocoa è l'ambiente di sviluppo. Cocoa è una suite integrata di
componenti software object oriented che permettono di creare rapidamente software
robusti e ricchi di caratteristiche, grazie alle classi (componenti) che possono essere
riutilizzate, modificate ed adattate alle necessità richieste dal software. Le classi Cocoa
esistono per quasi tutte le necessità di sviluppo, dalle interfacce utente alla formattazione
dei dati e nel caso sia necessario è possibile creare agevolmente sottoclassi. È importante
notare che Apple consiglia vivamente di sfruttare le classi e gli oggetti forniti da Cocoa, in
quanto restituisce agli utenti uniformità tra i software sviluppati, sopratutto per quanto
riguarda l'approccio alle interfacce grafiche.
Cocoa è un ambiente con cui è possibile sviluppare (limitatamente a Mac OS X), oltre ad
applicazioni, anche strumenti da riga di comando, plug-ins e qualunque tipo di software.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
40
E' possibile utilizzare diversi linguaggi di programmazione, anche se il linguaggio
privilegiato è Objective-C. Inoltre è possibile richiamare dal codice, interfacce di
programmazione non Cocoa, come la libreria di interfacce BSD (/usr/include); è possibile
anche mescolare codice C++ con codice Cocoa e collegarli nello stesso eseguibile.
Per lo sviluppo di applicazioni in Mac OS X è possibile sviluppare utilizzando bridge
come PyObjC (il bridge Python–Objective-C) e RubyCocoa (il bridge Ruby–Cocoa). Tali
bridge permettono di scrivere applicazioni cocoa nei rispettivi linguaggi, Python e Ruby,
interpretandoli e creando oggetti, in modo da poter comunicare con gli oggetti object-C
nativi dell'ambiente Cocoa.
Il cuore delle librerie di classi Cocoa (Cocoa Touch) è impacchettato in due framework
principali, Foundation ed Application Kit per Mac OS X e Foundation ed UIKit per
iPhone OS. Come tutti i Framework, non includono solo librerie dinamiche condivise, ma
header file, Documentazione API e altre risorse. Entrambi i framework seguono il
principio chiave di Cocoa nel dividere ciò che fa parte dell'interfaccia d'utente e ciò che
non ne fa parte.
3.1.1 Cocoa in Mac OS X
Si consideri il seguente diagramma semplificato di Mac OS X.
In Figura 2 è possibile osservare la struttura semplificata di Mac OS X. Si possono
distinguere i seguenti blocchi:
Figura 2: struttura semplificata
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
41
• Carbon è un set di API in C che offre agli sviluppatori un avanzato kit di strumenti per
interfacce utente, gestione degli eventi e supporto multiprocessing. Gli sviluppatori
hanno accesso alle altre API C e C++, incluso il sistema di disegno grafico OpenGL, al
microkernel Mach ed ai servizi BSD del sistema operativo. Tali Api consentono inoltre
a programmi scritti per sistemi Classic Mac OS 8.1 (o precedenti) di funzionare anche
su Mac OS X, quindi garantisce la retrocompatibilità di software esistente. È molto
utile quindi per sviluppare una nuova applicazione o effettuare un porting da un'altra
piattaforma. In ogni caso se si deve sviluppare una nuova applicazione e si desidera
usare un linguaggio orientato agli oggetti, è preferibile utilizzare Cocoa.
• Aqua definisce l'aspetto e le caratteristiche estetiche delle applicazioni in Mac OS X.
Aqua incorpora colori, profondità, colori traslucidi e texture complesse nelle interfacce
grafiche. La funzione di Aqua è quindi rendere coerenza all'aspetto grafico delle
applicazioni, restituendo così familiarità nell'utilizzo da parte degli utenti. Tutte le
applicazioni scritte utilizzando le interfacce di Mac OS X ereditano automaticamente la
caratteristiche Aqua.
• Quartz è un potente sistema grafico che costituisce la struttura dei modelli grafici per
Mac OS X. Quartz offre un sofisticato motore di disegno bidimensionale ed un
avanzato ambiente per la gestione delle finestre per le applicazioni. Inoltre, il motore di
disegno include anche il disegno di Portable Document Format (PDF) ed offre
strumenti di disegno per funzionalità di Mac OS X. I servizi per le finestre offerto da
Quartz forniscono funzionalità di basso livello come buffering delle finestre, creazione
dinamica, ombre, traslucenza ed altri effetti inseriti nelle interfacce grafiche di Aqua.
• OpenGL (Open Graphics Library) è una specifica che definisce una API per più
linguaggi e per più piattaforme per scrivere applicazioni che producono computer
grafica 2D e 3D. È usata principalmente per la programmazione di giochi, animazioni,
CAD/CAM, immagini mediche ed altre applicazioni che visualizzano e manipolano
grafiche 2D e 3D ad alte prestazioni. L'implementazione Apple di OpenGL è stata
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
42
ottimizzata per piattaforme Machintosh ed include una suite di estensioni che danno
agli sviluppatori accesso ad avanzate capacità hardware per la grafica.
• QuickTime, le Api Cocoa per QuickTime includono classi per usare applicazioni in
Mac OS X che includono caratteristiche multimediali come video, audio, animazioni,
grafica, testo e funzionalità di interattività. QuickTime supporta la maggior parte dei
formati per immagini, video ed audio, incluso video MPEG-4 ed audio AAC.
• La figura 2 trascura però diverse parti del Sistema Operativo in questione, ma rende
bene l’idea di come Cocoa interagisca con il sistema stesso.
La Figura 3 evidenzia invece come Cocoa sia inserito nell'architettura di sistema. Tale
schema mostra come Mac OS X sia composto da una serie di strati software che partono
dalle radici di Darwin11 ed arrivano fino agli ambienti applicativi. Gli strati intermedi
rappresentano il software di sistema incluso nei due framework principali: Core Services e
Application Services. Come si evince dal grafico ogni componente ha interazione con lo
strato sottostante.
Le due figure sono in sostanza molto simili, ad esempio Quartz (realizzato nel framework
Core Graphics), il componente di sistema che è in gran parte responsabile del rendering
11 Darwin è la tecnologia su cui si basa il Core OS di Mac OS X ed integra diverse tecnologie come descritto nel
paragrafo 5 del capitolo 2.
Figura 3: Architettura di Mac OS X
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
43
con l'interfaccia utente Aqua, fa parte dello strato Application Services. Alla base della
struttura c'è Darwin: tutto in Mac OS X, compreso Cocoa, dipende dalle funzioni di
Darwin.
In figura 4 è possibile osservare le classi Cocoa divise in pacchetti, in particolare è
possibile vedere le dipendenze dei subframework di Cocoa dagli altri strati di Mac OS X.
Apple ha progettato Cocoa in modo da permettere, attraverso le sue interfacce, l'accesso
diretto alle tecnologie basilari di cui le applicazioni in genere hanno bisogno. E' possibile
comunque avere controllo anche di funzionalità non supportate dalle interfacce Cocoa, ad
esempio richiamando le funzioni di Core Graphics o quelle di OpenGL. Fortunatamente,
l'uso dei livelli inferiori del framework non è un problema perché le interfacce di
programmazione sono scritte in standard ANSI C.
I principali framework da cui dipende Cocoa o che richiama attraverso le sue classi e
metodi sono i seguenti:
• Core foundation: Molte classi del Framework Foundation sono strettamente legate al
Core Foundation12, il quale, in alcune sue parti è basato su BSD, in altre su di una parte
dello strato Darwin.
12 Core Foundation fa parte del Core Services ed è descritto nel paragrafo 2.1.2
Figura 4:dipendenze dei subframework di Mac OS X
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
44
• Carbon: Cocoa accede per diverse esigenze in Carbon, questo perchè molti servizi
Carbon, come system-wide services, si trovano nello strato Core Services ed in
Application Services.
• Core Graphics: Le classi Cocoa di immagini e di disegno sono strettamente basate sul
framework Core Graphics, che implementa Quartz ed il Window Server.
• Launch Services: La classe NSWorkspace realizza i servizi di Launch Services.
Inoltre, Cocoa utilizza le caratteristiche application-registration di Launch Services per
associare le icone alle applicazioni ed ai documenti.
• Print Core: La classe di stampa Cocoa presenta un'interfaccia orientata agli oggetti
verso il sottosistema di stampa.
3.1.2 Cocoa in iPhone OS
Nonostante l'infrastruttura di iPhone OS e la sua interazione con Cocoa siano simili a Mac
OS X, bisogna considerare significative differenze. Confrontando la Figura 5, di iPhone
OS, e figura 2, di Mac OS X, si può notare come la struttura di iPhone è una serie di strati
che vanno da Core OS foundation ad Application Framework, il cui strato più critico per le
applicazioni è UIKit.
Come nella stratificazione di Mac OS X, anche lo strato intermedio di iPhone OS consiste
Figura 5: Stratificazione di iPhone
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
45
nelle librerie grafiche ed audio, anche in questo caso ogni strato spesso ha dipendenze
dagli strati inferiori.
In generale possiamo dire che il sistema di librerie e framework di iPhone OS è un
sottoinsieme delle librerie e dei framework di Mac OS X. Ad esempio, non vi è alcuna
necessità di sviluppare applicazioni Carbon in ambiente iPhone OS, così come non vi è
alcuna necessità di accesso da riga di comando (ambiente BSD); non vi sono servizi e
framework di stampa, e QuickTime è assente dalla piattaforma. Tuttavia, a causa della
natura delle periferiche supportate da iPhone OS, vi sono alcuni framework, sia pubblici
che privati, specifici per iPhone OS.
Di seguito saranno descritti i framework che si trovano in ogni strato di iPhone OS:
Core OS: Questo livello contiene il kernel, il file system, infrastruttura di rete, security,
power management, e un certo numero di driver di periferica. Inoltre include la libreria
libSystem, che gestisce le specifiche POSIX / BSD 4.4/C99 API e include API per molti
servizi a livello di sistema.
Core Services: Il framework a questo livello fornisce servizi essenziali (core services),
come la manipolazione delle stringhe, Networking, URL Utilities, gestione dei contatti, e
le preferenze.
Questo strato comprende Core Foundation, un framework che fornisce astrazioni per tipi
di dati comuni (come stringhe e collezioni), consentendo un elevato grado di integrazione
tra object-oriented e codice di procedura.
Media: I servizi ed i framework in questo strato dipendono dal livello Core Services e
forniscono servizi di grafica e servizi multimediali allo strato Cocoa Touch. Comprendono
Core Graphics, OpenGL ES, Core Animation, Core Audio.
Cocoa Touch: Il framework in questo strato supporta direttamente applicazioni basate su
iPhone OS. E' costituito da due framework Objective-C fondamentali per lo sviluppo di
applicazioni in iPhone OS:
• UIKit fornisce gli oggetti da visualizzare nelle interfacce d'utente delle applicazioni e
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
46
definisce il comportamento delle applicazioni, compreso la manipolazione degli eventi
ed il disegno.
• Foundation definisce la struttura base degli oggetti, stabilisce il meccanismo per la
loro gestione e fornisce gli oggetti fondamentali per i tipi di dati, collection e servizi del
sistema operativo. Foundation è essenzialmente il gemello del framework Core
Foundation.
Generalmente è possibile, attraverso metodi o funzioni Foundation o IUKit, accedere alle
funzionalità degli strati inferiori del sistema; se invece si ha bisogno di caratteristiche che
non sono risolvibili attraverso quelle API, è possibile scegliere di usare direttamente un
framework di uno strato inferiore. Nonostante UIKit, per esempio, possa gestire il Web Kit
per disegnare i testi, comunque è possibile decidere di accedere direttamente al Core
Graphics per disegnarli in quanto può servire per gestire determinate esigenze. E' possibile
accedere ai framework degli strati inferiori proprio in quanto le interfacce sono scritte in
ANSI C, perfettamente compatibile con Objective-C.
3.2 Ambiente di Sviluppo
L'ambiente di sviluppo per applicazioni Cocoa non è unico, è possibile infatti sia
sviluppare con applicazioni fornite da Apple come Xcode13 ed Interface Builder, che
utilizzare software di terze parti. Ad esempio, per sviluppare software per Mac OS X
basate su Carbon, si può scrivere codice usando un text editor come Emacs, compilare
l'applicazione dalla linea di comando ed utilizzare gdb debugger per il debug
dell'applicazione stessa.
Nonostante ciò, Xcode ed Interface Builder sono le applicazioni più comunemente
utilizzate per sviluppare software Cocoa. Nate insieme a Cocoa stesso, esiste una grande
compatibilità tra tool e framework, facilitando così il disegno, la gestione, la compilazione
13 Xcode è talvolta usato riferendosi all'intera suite di strumenti di sviluppo e framework, altre volte invece, è utilizzato
specificatamente per l'IDE che permette di gestire codice e progetti eseguibili.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
47
ed il debug di progetti software.
3.2.1 Piattaforma SDK
Con l'introduzione di Xcode 3.1 e dell'iPhone OS, per creare un progetto software bisogna
scegliere la piattaforma SDK, che permetterà di costruire software eseguibile per
determinate versioni di Sistemi Operativi o diverse piattaforme, come Mac OS X e iPhone
OS. Mac OS X SDK, ad esempio, è formato da framework, librerie, header file e strumenti
di sistema. L'SDK per iPhone include gli stessi componenti, in più comprende altri tool ed
un compilatore specifico per iPhone, inoltre contiene anche uno specifico ambiente di
simulazione per iPhone OS. L'SDK contiene inoltre diversi template di progetti specifici
per le diverse piattaforme.
3.2.2 Xcode
Xcode è il motore dell'ambiente di sviluppo integrato (IDE) per applicazioni in Mac OS X
ed iPhone OS, inoltre è l'applicazione che cura la maggior parte delle fasi di sviluppo di un
progetto software, dalla fase iniziale fino alla conclusione dello sviluppo.
Xcode permette di:
• Creare e gestire progetti, indicando la piattaforma, i requisiti necessari, le dipendenze e
le configurazioni strutturali
• Scrivere codice in un editor che gode di caratteristiche come la colorazione del codice
ed il rientro automatico
• Navigare e cercare attraverso i componenti di un progetto, incluso gli header file e la
documentazione
• Compilare un progetto
• Effettuare il Debug di un progetto remotamente, o localmente nel simulatore iPhone
OS, in un grafico source-level debugger
Xcode compila progetti da codice sorgente scritto in C, C ++, Objective-C, e Objective-C
++, genera eseguibili e tutti i tipi di software supportati da Mac OS X, compresi strumenti
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
48
da riga di comando, framework, plug-in, estensioni del kernel, bundle e le applicazioni.
Per iPhone OS, invece, sono possibili solo applicazioni eseguibili.
Xcode permette una quasi illimitata personalizzazione di strumenti di sviluppo e debug,
packaging di eseguibili, costruzione di procedure (compresi i copy-file, script-file, ed altre
fasi di costruzione del progetto), e dell'interfaccia utente (compreso un editor separato di
codice multi-view). Supporta inoltre molti sistemi di gestione del codice sorgente, come
CVS, Subversion e Perforce, che permettono di caricare il codice in un repository,
effettuare modifiche, aggiornare le versioni e confrontarle.
Xcode, essendo specifico per sviluppare applicazioni Cocoa, permette di scegliere, alla
creazione di un progetto, a quale template far riferimento, applicazioni, tool, bundle,
framework o altri. Per compilare applicazioni per Mac OS X, Xcode usa GNU C compiler
(gcc) e per debugging utilizza GNU source-level debugger (gdb); inoltre è ben integrato
con Interface builder, applicazione di sviluppo per interfacce.
3.2.3 Flusso di lavoro
La differenza nello sviluppo di applicazioni per Mac OS X e per iPhone OS non è negli
strumenti ma nel flusso di lavoro dello sviluppo. In Mac OS X uno sviluppo tipico segue il
flusso descritto in seguito:
• Creare un progetto in Xcode usando un template di Mac OS X SDK.
• Scrivere il codice e, usando Interface Builder, costruire la sua interfaccia d'utente.
• Definire gli obiettivi ed eseguirli nell'ambiente di sviluppo del progetto.
• Testare e effettuare il debug dell'applicazione usando le facilitazioni che Xcode offre;
vedere il debug nella finestra di Console.
• Misurare le prestazioni dell'applicazione usando uno o più strumenti per analizzarla.
In iPhone OS, le fasi dello sviluppo di un'applicazione è leggermente più complicato.
Prima di iniziare comunque è necessario registrarsi come sviluppatore della piattaforma.
Successivamente, lo sviluppo di un'applicazione passa attraverso i seguenti passi:
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
49
1. Configurare la periferica, sono necessari quindi strumenti, framework ed altri
componenti installati sulla periferica stessa.
2. Creare un progetto in Xcode usando un template da iPhone OS SDK.
3. Scrivere il codice e, usando Interface Builder, costruire la sua interfaccia d'utente.
4. Definire gli obiettivi ed eseguirli nell'ambiente di sviluppo del progetto.
5. Compilare l'applicazione.
6. Testare ed effettuare il debug dell'applicazione localmente in iPhone OS Simulator
oppure remotamente sul dispositivo, in questo caso il debug dell'eseguibile sarà sul
dispositivo stesso, sarà anche possibile vedere il debug nella finestra di Console.
7. Misurare le prestazioni dell'applicazione usando uno o più strumenti.
3.2.4 Interface Builder
La seconda grande applicazione di sviluppo per progetti Cocoa è Interface Builder (Apple,
The development Environment), uno strumento grafico per la creazione di interfacce
utenti. Tale strumento è anche utile per creare interfacce utente per applicazioni Carbon in
Mac OS X.
I quattro principali elementi di design su cui è centrato Interfacce Builder sono:
Nib file. Un Nib file è un file contenitore (una directory trasparente) che contiene gli
oggetti che compaiono nell'interfaccia utente in una forma di archivio. In sostanza, questo
archivio è un oggetto grafico che contiene informazioni su ogni oggetto, compresa la
dimensione e la posizione, informazioni sui collegamenti tra gli oggetti e sulle classi
personalizzate. Quando si crea e si salva un'interfaccia utente di Interface Builder, tutte le
informazioni necessarie per ricreare l'interfaccia sono memorizzate nel nib file.
Nib file offre un modo semplice per localizzare le interfacce di utente. Interface Builer
infatti registra il nib file nel progetto Cocoa, quando tale progetto sarà compilato il nib file
sarà copiato nella locazione corretta all'interno del pacchetto creato.
Interface Builder visualizza il contenuto del nib file in una finestra del documento (nib file
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
50
windows), tale finestra permette di accedere agli oggetti del nib file, soprattutto a quelli
principali come finestre, menu, e objects controller, che collegano gli oggetti delle
interfacce di utente con i modelli di oggetti che rappresentano i dati delle applicazioni,
inoltre forniscono gestione globale dell'applicazione da sviluppare.
Object library. La finestra Library di Interface Builder contiene gli oggetti che
l'interfaccia utente può contenere, comprende sia gli oggetti tipici (come finestre, controlli,
menu, text view) che i controller di oggetti, viste personalizzate di oggetti, oppure
framework come Image Kit. Library raggruppa oggetti per categorie permettendo di
navigare al loro interno cercando oggetti specifici. Trascinando un oggetto dalla library su
di un'interfaccia Interface Builder crea un'istanza predefinita di tale oggetto in modo da
poterlo ridimensionare, configurare e collegarlo ad altri oggetti utilizzando la finestra
Inspector. (che diremo l'Inspector)
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
51
Inspector. Interface Builder ha la finestra Inspector associata agli oggetti dell'interfaccia
utente. Inspector include alcuni riquadri selezionabili per impostare la configurazione
iniziale di runtime degli oggetti (anche se le dimensioni ed altri attributi possono essere
manipolati direttamente). L'Inspector, in Figura 6, mostra gli attributi primari per il campo
testo, le varie sezioni racchiuse nell'etichetta permettono di settare altri parametri a vari
Figura 6: Inspector
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
52
livelli gerarchici. Oltre alle caratteristiche primarie degli oggetti è possibile tramite
Inspector selezionare effetti animati, gestire eventi AppleScript e vincolare gli oggetti con
meccanismi target-action. Tali meccanismi infatti vengono utilizzati da Cocoa per collegare
controlli dell'interfaccia grafica con oggetti del software che si sta sviluppando. In sostanza
è un tipo di comunicazione utilizzata all'interno delle interfacce grafiche per interpretare gli
input degli utenti e collegare tali controlli ad azioni progettate nel software. Tale sistema
risulta infatti più efficiente di altri meccanismi di comunicazione previsti dalla
programmazione classica (ad esempio notifiche, binding e delegati).
Connections panel. Connection Panel (Figura 7) è un pannello che permette di
visualizzare le connessioni legate ad un oggetto selezionato con il tasto destro nella finestra
di Interface Builder, riferita alla View da modificare. È possibile quindi, attraverso questo
pannello, visualizzare e gestire tutte le connessioni dell'oggetto selezionato.
Interface Builder è perfettamente integrato con Xcode, questo significa che conosce le
azioni, le proprietà e le classi utilizzate. Tale integrazione permette ad Interface Builder di
identificare eventuali modifiche al codice del progetto e riportarle così nell'interfaccia
d'utente.
Figura 7: Connection Panel
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
53
3.2.5 The iPhone OS Simulator
Per i progetti iPhone OS è possibile utilizzare iPhone Simulator come piattaforma SDK.
Quando si compila e si esegue un progetto, Xcode avvia il simulatore che visualizza
l'applicazione come se fosse sul dispositivo e abilita l'utilizzo dell'interfaccia utente, è
anche possibile utilizzare il Simulatore per il debug delle applicazioni prima di installare il
software nella periferica.
Bisogna sempre testare il software sul simulatore tenendo presente che non può
rappresentare perfettamente il dispositivo: il mouse ad esempio sostituirà il Touch
rendendo impossibile quindi l'utilizzo di interfacce Multitouch. Inoltre il simulatore non
usa versioni di OpenGL framework specifiche per iPhone OS, ma usa versioni per Mac
OS X di Foundation, Core Foundation, e CFNetwork frameworks, come la versione di
libSystem per Mac OS X.
E' importante inoltre non paragonare le prestazioni dell'applicazione sul simulatore
rispetto a quelle sul dispositivo, il simulatore infatti esegue le applicazione come fossero
applicazioni ospiti di Mac OS X, quindi avranno a disposizione una partizione di memoria
di 4 giga e lo spazio disponibile è quello che avrebbe a disposizione un processo,
prestazioni quindi sensibilmente superiori a quelle ottenute sul dispositivo reale.
3.2.6 Prestazioni delle Applicazioni e Tool
Anche se Xcode ed Interface Builder sono i principali strumenti utilizzati per lo sviluppo
di software Cocoa, ci sono decine di altre applicazioni utili, ad esempio gli strumenti per
diagnosticare le prestazioni delle applicazioni.
3.2.6.1 Instruments
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
54
Instruments è un'applicazione introdotta in Xcode 3.0 che permette di utilizzare molteplici
tester di performance simultaneamente e permette di visualizzare i risultati in grafici in
funzione del tempo. E' possibile visualizzare l'utilizzo della CPU, scrittura e lettura su
disco, memoria utilizzata, attività dei thread, statistiche di rete, utilizzo di file e cartelle ed
altre misurazioni, con la possibilità di combinarle in grafici in funzione del tempo. La
rappresentazione simultanea dei diversi dati rilevati permette di scoprire le relazioni tra ciò
che si è misurato.
3.2.6.2 Shark
Shark è un'applicazione di analisi delle performance, crea un profilo time-based dei
software in esecuzione. In una finestra temporale, infatti, traccia il grafico dell'allocazione
in memoria delle applicazioni. Shark permette di tracciare informazioni per una singola
applicazione o di tutto il sistema, in Mac OS X è possibile includere i componenti del
Figura 8: Instruments Window
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
55
kernel come driver ed estensioni del kernel stesso. Inoltre, è possibile tramite Shark
monitorare le chiamate al file system, tracciare le chiamate di sistema e l'allocazione della
memoria, analisi statiche del codice e raccoglie informazioni su cache misses, page fault
ed altre metriche di sistema. Infine, Shark supporta l'analisi di codice scritto in C,
Objective-C, C++ ed altri linguaggi.
3.2.6.3 Altre applicazioni per le prestazioni (Mac OS X)
Molte altre applicazioni sono utili alla misurazioni ed analisi delle performance di
applicazioni per Mac OS X. Alcuni esempi sono:
• Thread Viewer visualizza l'attività di comunicazione tra i thread di un processo,
visualizzando su di una linea temporale l'attività di ogni thread e colorando
diversamente ogni azione. Selezionando una time-line è possibile prendere un
campione dell'attività in quel punto.
• BigTop visualizza in un grafico il trend delle prestazioni nel tempo, visualizzando in
tempo reale l'uso della memoria, i page faults, l'uso della CPU ed altri dati.
• Spin Control campiona automaticamente le mancate risposte dell'applicazione.
Lasciando in background Spin Control mentre l'applicazione è in esecuzione, nel
momento in cui, dopo un determinato input, non si riceve risposta dal software appare il
cursore di spinning, e Spin Control campiona automaticamente l' applicazione,
raccogliendo le informazioni su cosa sta facendo all'applicazione in esecuzione.
• MallocDebug visualizza tutti i blocchi allocati dell'applicazione, organizzati in
funzione dello stack al momento dell'allocazione; è possibile quindi osservare il
consumo di memoria dell'applicazione in esecuzione, dove vengono allocati i blocchi e
quale funzione alloca più memoria. MallocDebug aiuta a trovare memoria allocata che
non è puntata nel software, aiutando così a rintracciare dove la memoria viene allocata.
• QuartzDebug è uno strumento di aiuto per come si visualizza il debug delle
applicazioni. Esso è utile per applicazioni che hanno un gran numero di disegni ed
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
56
immagini, gestendo numerose opzioni di debug.
3.3 Cocoa Framework
Prima di studiare il framework Cocoa è necessario osservare che le applicazioni Cocoa
non sono basate tutte sullo stesso linguaggio perchè possono essere scritte in diversi
linguaggi e non hanno in comune i software di sviluppo in quanto è possibile compilarli e
scriverli da riga di comando. Possiamo quindi affermare che le applicazioni Cocoa hanno
in comune l'essere composte da oggetti ereditati dalla classe principale NSObject e sono
basati su runtime Objective-C. Questo vale per tutti i framework Cocoa. Bisogna
comunque sottolineare che Foundation Framework prevede anche un'altra classe di root,
NSProxy, ma questa viene utilizzata raramente nello sviluppo di applicazioni Cocoa.
Inoltre, si deve sottolineare che è possibile costruire una classe principale personalizzata,
ma richiede molto lavoro, dovendo scrivere codice che interagisce con il runtime
l'Objective-C.
Sono diversi i framework sviluppati per applicazioni Cocoa, sia Apple che di terze parti,
ma in sostanza è possibile affermare che soltanto due sono i framework essenziali nello
sviluppo di applicazioni. Tali framework si differenziano per Mac OS X ed iPhone OS e
sono:
1. Per Mac OS X: Foundation ed Application Kit
2. Per iPhone OS: Foundation ed UIKit
I framework Foundation, Application Kit, e UIKit sono essenziali nello sviluppo Cocoa,
tutti gli altri framework sono invece di supporto allo sviluppo. Infatti, non è possibile
sviluppare un'applicazione Cocoa per Mac OS X senza collegarsi (ed usare classi) di
Application Kit, come non è possibile sviluppare applicazioni per iPhone OS senza
collegarsi a UIKit. Allo stesso tempo, non è possibile sviluppare alcuna applicazione senza
utilizzare classi del framework Foundation. Il collegamento al giusto framework su Mac
OS X avviene automaticamente appena ci si collega ad uno dei framework Cocoa per Mac
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
57
OS X. Inoltre, bisogna notare che funzioni, tipi di dato e costanti in Foundation ed in
Application Kit hanno “NS” come prefisso, in UIKit hanno invece “UI” come prefisso.
Nella versione 10.5 di Mac OS X, inoltre, il framework Cocoa supporta l'indirizzamento a
64 bit, come iPhone OS. Tale risultato è stato ottenuto sopratutto modificando le API
Cocoa, le modifiche più significative riguardano l'introduzione dei tipi NSInteger e di
NSUInteger, in luogo di insigned int e di tipi CGFloat.
Per conoscere bene l'ambiente iPhone OS è necessario approfondire anche l'ambiente Mac
OS X, in quanto iPhone OS è un sottoinsieme delle sue librerie e della sua struttura. Di
conseguenza per conoscere UIKit è necessario soffermarsi anche su Application Kit. In
iPhone OS, lo strato Cocoa Touch comprende i framework Foundation e UIKit.
3.3.1 Foundation
Il framework Foundation definisce uno strato base di classi che possono essere usate per
ogni tipo di software Cocoa. Il criterio con cui una classe appartiene a Foundation
piuttosto che ad Application Kit è legato all'interfaccia. In sostanza, se un oggetto non
appare nell'interfaccia di utente o non è esclusivamente usato a supporto dell'interfaccia di
utente, significa che appartiene a Foundation. Inoltre, è possibile creare software Cocoa
utilizzando solo Foundation senza altri framework, ad esempio per strumenti a riga di
comando o per server Internet.
Gli scopi per cui è stato pensato Foundation sono:
• Definire il comportamento di oggetti basilari introducendo convenzioni coerenti per più
caratteristiche, come la gestione della memoria, mutabilità degli oggetti e notifiche
• Supporto all'internazionalizzazione ed alla localizzazione attraverso tecnologie bundle
e stringhe Unicode
• Fornire supporto alla persistenza degli oggetti
• Fornire supporto agli oggetti distribuiti
• Fornisce misure di indipendenza dal Sistema Operativo, orientato alla portabiltà
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
58
• Offrire oggetti wrapper (o simili) per la programmazione di primitive, come valori
numerici, stringhe e collection. Inoltre, include classi per accedere ai servizi sistema di
base come porte, threads e file system
Le applicazioni Cocoa, che per definizione devono essere collegate al framework
Application kit oppure al UIKit, devono essere ben collegate anche al framework
Foundation. Nella gerarchia delle classi hanno la stessa classe fondamentale, NSObject, e
molti metodi e funzioni di Application Kit e UIKit hanno oggetti di Foundation come
valori di ritorno dei parametri. Molte classi di Foundation possono inoltre sembrare
costruite per le applicazioni, ad esempio NSUndoManager e NSUserDefaults, ma sono
incluse in Foundation perchè il loro utilizzo non coinvolge l'interfaccia grafica dell'utente.
3.3.1.1 Foundation: paradigmi e politiche
Foundation introduce politiche e paradigmi per la programmazione Cocoa, al fine di
garantire coerenza nel comportamento degli oggetti di un programma in determinate
situazioni. Questi includono:
Mantenimento e rilascio di oggetti. Il runtime Objective-C e Foundation offrono ai
programmi Cocoa due metodi per garantire il mantenimento degli oggetti quando servono
ed il loro rilascio quando non è più necessario mantenerli. Tali metodi sono Garbage
Collection (non supportato da iPhone OS) e Memory Management. Garbage Collection,
introdotto in Objective-C 2.0, automaticamente si disfa delle tracce di cui non ha più
bisogno, in modo da liberare memoria. Memory anagement, invece, istituisce una politica
di proprietà degli oggetti, ritiene in sostanza gli oggetti responsabili del rilascio di altri
oggetti che hanno creato, copiato o esplicitamente trattenuto.
E' da notare che in iPhone OS, per motivi di prestazioni, Garbage Collection non è
abilitato nelle applicazioni Cocoa. Memory Management comunque è descritto nel
paragrafo 4.3.4.1
Mutable class variants. In Foundation, molti valori e contenitori di classi sono varianti di
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
59
classi immutabili. Ogni classe mutevole è sempre una sottoclasse di una immutabile. Se
bisogna cambiare dinamicamente un valore incapsulato o associato ad un oggetto, è
possibile creare un'istanza di una classe mutevole; poiché è ereditato da una classe
immutabile, è possibile passare l'istanza mutevole in metodi mutuati dai type immutabili.
Class clusters. Una Class Cluster è una classe astratta ed un set privato di sottoclassi per
le quali la classe astratta agisce da interfaccia come un ombrello. In funzione del contesto
(in particolare dal metodo che si utilizza per creare un oggetto) restituisce un'istanza o una
sottoclasse ottimizzata appropriatamente. Struttura Gerarchica è descritta nel paragrafo
4.4.9.1.
Notifications. Notification è uno dei principali modelli di progettazione in Cocoa, è basato
su di un meccanismo di broadcast che permette agli oggetti, detti osservatori, di prendere
informazioni su cosa un altro oggetto sta facendo o a quali eventi di sistema o dell'utente
sta andando incontro. L'oggetto osservato può non essere a conoscenza dell'esistenza e
dell'identità dell'osservatore. Ci sono diversi tipi di notifiche: sincrona, asincrona e
distribuita. Il meccanismo di notifica di Foundation è implementato dalle classi
NSNotification, NSNotificationCenter, NSNotificationQueue, e
NSDistributedNotificationCenter.
3.3.1.2 Classi Foundation
La gerarchia delle classi Foundation, hanno radice nella classe NSObject, che (insieme ad
i protocolli NSObject e NSCopying) definisce gli attributi ed il comportamento degli
oggetti base.
Il resto del framework Foundation è composto da diversi gruppi di classi, più qualche
classe individuale. Ci sono classi che rappresentano tipi di dato basilari, come stringhe e
array di bit, collezioni di classi per registrare altri oggetti, classi per rappresentare
informazioni di sistema come date, oppure classi per rappresentare entità di sistema come
porte, thread e processi. La gerarchia di classi rappresentate in figura 9, 10 e 11 descrivono
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
60
la divisione in gruppi e la gerarchia associata alle classi. Le classi nelle aree blu sono
quelle condivise dalle versioni Foundation di Mac OS X ed iPhone.
Le figura 9, 10 e 11 raggruppano le classi del framework Foudation nelle seguenti
categorie:
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
61
Figura 9: Foudation Framework (1)
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
62
Figura 10: Foundation Framework (2)
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
63
Figura 11: Foundation Framework (3)
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
64
Value objects. Value object include dati di vari tipi, restituisce accesso ai dati ed offre vari
tipi di manipolazione degli stessi, inoltre, in quanto oggetti, possono essere archiviati e
distribuiti. NSData fornisce una memorizzazione orientata agli oggetti per stringhe di
bytes mentre NSValue la fornisce per array di valori scalari semplici. Le classi NSDate,
NSCalendarDate, NSTimeZone, NSCalendar, NSDateComponents, e NSLocale
forniscono oggetti che rappresentano tempo, date, calendari e Locales. Offrono metodi per
calcolare differenze temporali, per visualizzare date e tempi in diversi formati e per
aggiustare tempo e date in funzione dei fusi orari.
Strings. NSString è un altro tipo di valore che fornisce una memorizzazione orientata agli
oggetti per array di byte null-terminated in particolari codifiche. Include supporto per
conversione di stringhe codificate in UTF-16, UTF-8 e altri tipi di codifiche.
NSString inoltre offre metodi di ricerca, combinazione e comparazione di stringhe e per
manipolazione di percorsi di file system. Gli oggetti NSScanner permettono di analizzare
numeri e word provenienti da un oggetto NSString. NSCharcterSet rappresenta invece un
set di caratteri usati da diversi metodi NSString e NSScanner.
Collections. Un oggetto Collection registra ed utilizza altri oggetti (generalmente valori),
in un particolare ordine. NSArray utilizza un'indicizzazione zero-based, NSDictonary usa
coppie di valori chiave e NSSet memorizza in maniera non ordinata oggetti. Con un
oggetto NSEnumerator è possibile l'accesso sequenziale di elementi di una collezione. Gli
oggetti Collection sono componenti essenziali delle liste di proprietà e, come tutti gli
oggetti, possono essere archiviati e distribuiti.
Servizi Operating-system. Molte classi Foundation facilitano l'accesso a diversi servizi
degli strati inferiori del sistema operativo e, allo stesso tempo, isola lo sviluppatore dalle
idiosincrasie del sistema operativo. Per esempio, NSProcessInfo consente di interrogare
l'ambiente nel quale l'applicazione viene eseguita e NSHost restituisce il nome e l'indirizzo
dell'host nella rete. Un oggetto NSTimer invece invia un messaggio ad un altro oggetto a
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
65
determinati intervalli di tempo, NSRunLoop permette di gestire le sorgenti d input di
un'applicazione o altri tipi di programmi. NSUserDefaults fornisce un'interfaccia di
programmazione per database di valori di default globali o per utente.
File system and URL. NSFileManager fornisce un'interfaccia per le operazioni su file
come creazione, rinomina, cancellazione e spostamento. NSFileHandle permette
operazioni su file a basso livello (per esempio ricerca nei file). NSBundle ricerca risorse
memorizzate in bundle ed alcune di queste può caricarle dinamicamente, per esempio nib
file. E' possibile utilizzare NSURL e le classi relative per rappresentare, accedere e gestire
fonti URL di dati.
Multithreading, operations, e subtasks. NSThread aiuta nello sviluppo di programmi
multithread e varie classi offrono meccanismi per accedere al controllo delle risorse dei
thread concorrenti. NSOperation e NSOperationQueue realizzano operazioni multiple,
concorrenti e non concorrenti, in ordine di dipendenza e di priorità. Con NSTask, è
possibile creare un processo figlio che esegue compiti e monitorizza i progressi. É utile
ricordare che, nonostante NSThread sia l'interfaccia principale per creare thread nelle
applicazioni Cocoa, è possibile utilizzare anche thread Posix ogni qual volta lo si ritiene
utile.
Comunicazione interprocesso. Molte delle classi in questa categoria presentano vari
generi di porte di sistema, socket, e server name e generalmente sono implementate in IPC
di basso livello. NSPipe rappresenta una pipe BSD, un canale di comunicazione
unidirezionale tra processi. E' importante sottolineare che le classi dei name server non
sono presenti nella versione Foundation di iPhone OS.
Networking. Le classi NSNetService e NSNetServiceBrowser forniscono l'architettura
Zeroconf chiamata Bonjour, è un potente sistema di pubblicazione e ricerca di servizi su
rete IP.
Notifications. Racchiude le classi sintetizzate in “Foundation: paradigmi e politiche”
3.3.1.1.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
66
Archiving e serialization. Le classi di questa categoria rendono possibile la persistenza
degli oggetti distribuiti. NSCoder e le sue sottoclassi, insieme al protocollo NSCoding
rappresenta i dati contenuti in un oggetto in un'architettura indipendente in modo da
permettere la registrazione di classe delle informazioni insieme ai dati. NSKeyedArchiver
e NSKeyedUnarchiver offre metodi per codificare oggetti, valori scalari, e decodificarli in
modo indipendente dall'ordine di codifica dei messaggi.
Servizi per linguaggi Objective-C. NSException e NSAssertionHandler forniscono una
soluzione orientata agli oggetti di manipolare asserzioni ed eccezioni nel codice. Un
oggetto NSInvocation è una rappresentazione statica di un messaggio Objective-C che il
programma può registrare ed invocare successivamente in un altro oggetto, è usato da
NSUndoManager e da Distributed Objects system. un oggetto NSMethodSignature
registra le informazioni dei tipi di un metodo e le usa nello scambio di messaggi.
NSClassDescription è una classe astratta per definire e ricercare le relazioni e le proprietà
di una classe.
3.3.2 Application Kit (Mac OS X)
Application Kit è un framework che contiene tutti gli oggetti necessari
all'implementazione della grafica, e delle combinazioni di eventi dell'interfaccia grafica:
finestre, pulsanti, menu, scroller, campi di testo e così via. Application Kit gestisce
efficacemente tutti i dettagli a schermo, comunicando con le periferiche e ripulendo le aree
dello schermo prima di visualizzare clip e immagini. Il numero delle classi di Application
Kit è elevato ma nella realtà molte di queste sono richiamate indirettamente, è possibile
comunque scegliere a che livello di profondità accedere ad Application kit:
• Attraverso Interface Builder è possibile creare connessioni tra gli oggetti
dell'interfaccia utente e gli oggetti controllati dall'applicazione. Le connessioni mettono
in relazione gli oggetti gestiti dall'utente attraverso l'interfaccia e coordinati dalla
struttura dati interna. Per esempio si potrebbe dover implementare un metodo da
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
67
invocare quando l'utente sceglie una voce di menu.
• Controllare direttamente l'interfaccia utente, necessita una discreta familiarità con classi
e protocolli di Application Kit, per esempio permettere all'utente il drag and drop di
un'icona da una finestra all'altra richiede molta programmazione e familiarità con il
protocollo NSDragging.
• Implementare gli oggetti in maniera personalizzata attraverso il subclassing di NSView
o di altre classi, è possibile così scrivere metodi personalizzati di disegno usando
funzioni grafiche. Le sottoclassi richiedono una conoscenza approfondita di come
funziona Application Kit.
3.3.2.1 Descrizione di Application Kit
Application Kit comprende più di 125 classi e protocolli. Tutte le classi derivano dalla
classe NSObject del framework Foundation. Il diagramma in Figura 12 descrive le
ereditarietà e relazioni delle classi di Application Kit.
Figura 13: Classi Application Kit
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
68
Come si evince l'albero della gerarchia è molto vasto ma poco profondo, infatti la distanza
massima dalla radice è di cinque classi, ma la maggior parte dell'albero non ha tale
profondità.
Alla radice del ramo più grande di Application Kit c'è la classe NSResponder, che
definisce una catena di responder, un lista ordinata di oggetti che rispondono ad eventi
degli utenti. Quando un utente clicca sul pulsante del mouse e seleziona una chiave, un
evento è generato e passato alla catena di responder alla ricerca di un oggetto che può
relazionarsi all'evento. Ogni oggetto che genera eventi deriva dalla classe NSResponder.
Le classi fondamentali di Application Kit (NSApplication, NSWindow, and NSView)
hanno come radice NSResponder.
Il secondo ramo più grande di classi discende da NSCell. Bisogna notare che questo
gruppo di classi è pressappoco uno specchio delle classi che sono ereditate da NSControl,
che deriva da NSView. Per gli oggetti dell'interfaccia utente che rispondono alle azioni
degli utenti, Application Kit usa un'architettura che divide il lavoro in controllo degli
oggetti e celle di oggetti. Le classi NSControl, NSCell e le loro sottoclassi definiscono un
insieme comune di oggetti per interfacce utente (come bottoni, cursori e browser) che
l'utente può utilizzare per manipolare determinate funzionalità dell'applicazione. Molti
controlli di oggetti sono associati con una o più celle di oggetti che implementano dettagli
grafici e gestione di eventi. Per esempio, un pulsante è costituito da un oggetto NSButton
ed un oggetto NSButtonCell.
Controlli e celle implementano meccanismi che si basano su importanti pattern di
Application Kit: i meccanismi target-action. Una cella può memorizzare un'informazione
che identifica il messaggio che potrebbe essere inviato ad un oggetto particolare quando
l'utente clicca (o con altri tipi di interazione) la cella stessa. Quando un utente utilizza un
controllo, ad esempio cliccando con il puntatore del mouse, il controllo estrae
l'informazione richiesta dalla cella ed invia un messaggio all'oggetto target. Target-action
restituisce senso all'azione dell'utente specificando l'oggetto di destinazione ed il metodo
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
69
che dovrebbe invocare. Tipicamente si usa Interface Builder per settare target ed azioni
attraverso controlli a trascinamento da oggetti control verso l'applicazione o verso altri
oggetti, comunque è possibile settare target ed azioni anche attraverso la programmazione.
Un altro importante modello di design è attraverso il meccanismo di delega. Molti oggetti
in un'interfaccia d'utente, come campi testo e viste tabellari, definiscono una delega. La
delega è un oggetto che agisce in conto, oppure in coordinazione, dell'oggetto che lo ha
delegato, è quindi in grado di impartire specifiche delle applicazioni per la logica di
funzionamento dell'interfaccia utente.
Una caratteristica generale di Mac OS X 10.5 e sistemi successivi è l'indipendenza dalla
risoluzione: La risoluzione del monitor è disaccoppiato dal disegno fatto nel codice. Il
sistema automaticamente lavora il contenuto per il rendering a schermo. Le classi
Application Kit supportano risoluzioni indipendenti dagli oggetti dell'interfaccia utente.
3.3.2.2 Classi generali per le interfacce utenti
Le seguenti classi sono a disposizione per le interfacce utenti:
• Oggetto global application. Ogni applicazione usa una istanza di NSApplication che
controlla l'evento principale, tiene traccia delle finestre e menu dell'applicazione,
distribuisce gli eventi agli oggetti appropriati (essa stessa o una delle sue finestre),
riceve notifiche di eventi al livello di applicazione. Un oggetto NSApplication ha un
delegato notificato quando un'applicazione si avvia o termina, è nascosta o attivata,
potrebbe aprire un file selezionato dall'utente e così via. Con l'impostazione del
delegato dell'oggetto di NSApplication ed implementando i metodi del delegato, è
possibile personalizzare le azioni dell'applicazione.
• Finestre e view. Le classi di finestre e view, NSWindow e NSView, discendono da
NSResponder, così sono destinate a rispondere delle azioni dell'utente. Un oggetto
NSApplication contiene una lista di oggetti NSWindow per ogni finestra aperta
dell'applicazione ed ogni oggetto NSWindow mantiene una gerarchia di oggetti
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
70
NSView. La gerarchia di view è usata per disegnare e manipolare eventi all'interno di
una finestra. Un oggetto NSWindow si occupa di eventi a livello di finestra, distribuisce
eventi nelle proprie view e fornisce aree di disegno per le proprie view. Un oggetto
NSWindow ha anche un delegato che permette di personalizzare il comportamento. A
Partire dalla versione 10.5 di Mac OS X le classi delle finestre e delle view di
Application Kit supportano caratteristiche potenziate di animazioni.
• NSView è la superclasse per tutti gli oggetti di una finestra. Tutte le sottoclassi che
implementano disegni usano metodi grafici; drawRect: è il metodo principale che viene
sovrascritto quando si crea un nuovo oggetto NSView.
• Classi Controller per Cocoa bindings. La classe astratta NSController e le sue
sottoclassi NSObjectController, NSArrayController, NSDictionaryController, e
NSTreeController sono parti dell'implementazione di Cocoa bindings. Questa
tecnologia sincronizza automaticamente i dati dell'applicazione registrati negli oggetti e
la presentazione dei dati nell'interfaccia utente.
• Panels (dialogs). La classe NSPanel è una sottoclasse di NSWindow che visualizza
informazioni transitorie, globali o urgenti. Per esempio è possibile usare un'istanza di
NSPanel, piuttosto che una istanza di NSWindow, per visualizzare messaggi di errori.
Application Kit implementa alcune funzioni comuni riferite ai documenti come Save,
Open, Print.
• Menus e cursori. Le classi NSMenu, NSMenuItem, and NSCursor definiscono
l'estetica ed il funzionamento di un menu e dei cursori che l'applicazione offre
all'utente.
• Raggruppamento e scorrimento delle view. Le classi NSBox, NSScrollView, e
NSSplitView forniscono “accessori” grafici per visualizzare oggetti view o collection di
view nelle finestre. Con la classe NSBox è possibile raggruppare elementi in finestre e
disegnare un confine intorno all'intero gruppo. La classe NSSplitView aggiunge view
verticali ed orizzontali, attribuendo ad ognuna di esse una parte di territorio comune,
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
71
attraverso controlli, l'utente ridistribuisce il territorio alle view. La classe NSScrollView
e le sue classi d'aiuto, NSXlipView, fornisce un meccanismo di scrolling, nonché
oggetti grafici che permettono all'utente di controllare tali meccanismi. La classe
NSRulerView permette di aggiungere un righello e dei marcatori alle scroll view.
• Table view e outline view. La classe NSTableView fornisce dati in colonne e righe,
ideale anche per visualizzare tabelle di database, dove le righe sono i records e le
colonne sono gli attributi. L'utente può editare celle individuali e riadattare le colonne.
Impostando il delegato e l'oggetto origine dei dati è possibile controllare il
funzionamento ed il contenuto di un oggetto NSTableView. Outline view (per esempio
NSOutlineView o una sottoclasse di NSTableView) offre un altro approccio nella
visualizzazione tabulare dei dati. Con la classe NSBrowser è possibile creare un
oggetto con il quale gli utenti possono navigare e visualizzare i dati gerarchici.
3.3.2.3 Testo e caratteri
Il testo del sistema Cocoa è basato sul framework Core Text introdotto in Mac OS X 10.5.
Core Text fornisce una tecnologia, moderna, di basso livello ed alte prestazioni per la
stesura di testo. Utilizzando comunque il sistema Cocoa non è necessario accedere
direttamente a Core Text.
La classe NSTextField implementa un semplice input di testo in campo editabile, la classe
NSTextView invece prevede funzioni di editing più complesse per testi più estesi.
NSTextView, una sotto classe della classe astratta NSText, definisce l'interfaccia del
sistema esteso di testo. NSTextView supporta il testo esteso, allegati (grafica, file ed altro),
gestione degli input, key binding ed attributi del testo. Inoltre NSTextView lavora con
Font window e Font menu, stili di paragrafi, ordinamenti, appunti e permette la
personalizzazione e la delega, per tutto ciò è raro utilizzare sottoclassi di NSTextView.
Difficilmente è necessario creare istanze di NSTextView, considerando che nelle tavolozze
di Interface Builder come NSTextField, NSForm e NSScrollView contengono già oggetti
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
72
NSTextView.
E' possibile anche fare manipolazioni più potenti e creative del testo, come visualizzarlo in
un cerchio, utilizzando NSTextStorage, NSLayoutManager, NSTextContainer e le classi
relazionate. Il sistema di testo Cocoa supporta inoltre liste, tabelle e selezioni non
contigue.
Le classi NSFont e NSFontManager incapsulano e gestiscono le famiglie di font,
dimensioni e varianti. La classe NSFont definisce oggetti per ogni singolo Font; per
efficienza questi oggetti, che possono rappresentare parecchi dati, sono condivisi da tutti
gli oggetti delle applicazioni. La classe NSFontPanel definisce altresì i la finestra dei font
che viene presentata all'utente.
3.3.2.4 Grafica e colori
Le classi NSImage e NSImageRep includono i dati grafici, aiutando e migliorando
l'accesso alle immagini registrate in file ed alla visualizzazione a video delle stesse. Ogni
sotto classe di NSImageRep include tecniche per disegnare le immagini da particolari tipi
di dati origine. La classe NSImage invece fornisce rappresentazioni multiple della stessa
immagine e prevede anche comportamenti come il caching. Le funzionalità di disegno e
delle immagini di Cocoa sono integrate nel framework Core Image.
Il colore è supportato dalle classi NSColor, NSColorSpace, NSColorPanel, NSColorList,
NSColorPicker, e NSColorWell. NSColor e NSColorSpace supportano una notevole
quantità di formati e rappresentazioni di colori, tra cui quelle personalizzate. Le altre classi
sono per la maggior parte classi di interfaccia: definiscono e presentano all'utente pannelli
e view che permettono di selezionare ed applicare colori, per esempio, l'utente può
trascinare un colore dalla finestra Color.
Le classi NSGraphicsContext, NSBezierPath, e NSAffineTransform aiutano con il disegno
vettoriale e per il supporto grafico come trasformazioni di scala, rotazioni e traslazioni.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
73
3.3.2.5 Stampe e Fax
Le classi The NSPrinter, NSPrintPanel, NSPageLayout, e NSPrintInfo lavorano insieme
per fornire i mezzi per stampare o inviare per fax le informazioni che l'applicazione
visualizza nella sua finestre e nelle view, è anche possibile creare un file PDF di un
NSView.
3.3.2.6 Documenti e supporto al File-System
La classe NSFileWrapper serve a creare oggetti che corrispondono a file e directory nel
disco, registra inoltre il contenuto del file nella memoria in modo da renderlo disponibile
alla visualizzazione, alla modifica o passaggio ad un'altra applicazione, prevede anche
un'icona per il trascinamento del file o di una sua rappresentazione come un allegato. E'
anche possibile utilizzare questa classe in foundation per accedere ed elencare il contenuto
di file e directory. Le classi NSOpenPanel e NSSavePanel a fornire interfacce utente
comode per il file system.
Le classi NSDocumentController, NSDocument, e NSWindowController definiscono
un'architettura per creare applicazioni basate su documenti. Tali applicazioni gestiscono
facilmente capacità di salvataggio, ritorno, chiusura e gestione dei documenti.
3.3.2.7 Internazionalizzazione e Character Input Support
Se un'applicazione deve essere usata in più parti del mondo deve poter essere
personalizzata, o localizzata per lingua, paese, o regione culturale, per esempio
un'applicazione potrebbe aver bisogno di stringhe di caratteri, icone, contenuto dell'help e
nib file in versioni differenti per le varie lingue. File di risorse specifiche per particolari
linguaggi sono raggruppati in una sottodirectory di un bundle (la directory con estensione
.lproj), di solito si imposta la localizzazione delle risorse di file tramite Interface Builder.
Le classi NSInputServer e NSInputManager, insieme al protocollo NSTextInput, danno
accesso alle applicazioni al sistema di gestione dell'input. Tale sistema interpreta le battute
da tastiera di diverse tastiere internazionali e genera i caratteri appropriati o eventi per
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
74
visualizzare oggetti testo.
3.3.2.8 Operating-System Services
Application Kit fornisce servizi del sistema operativo di supporto alle applicazioni,
attraverso le seguenti classi:
• Condivisione dei dati con altre applicazioni. la classe NSPasteboard definisce il
pasteboard, un repository per i dati copiati dall'applicazione, rendendo questi dati a
disposizione di qualsiasi applicazione che deve utilizzarli. NSPasteboard implementa le
proprietà taglia-copia-incolla.
• Trascinamento. Attraverso poche righe di codice è possibile rendere gli oggetti
trascinabili e conformi ai protocolli NSDragging, NSDraggingDestination. Application
Kit nasconde tutti i dettagli di monitoraggio del cursore e dell'immagine trascinata.
• Controllo ortografico. La classe NSSpellServer consente di definire il servizio di
controllo ortografico e fornisce questo servizio alle altre applicazioni. per collegare
quindi all'applicazione il controllo ortografico bisogna usare la classe NSSpellChecker.
I protocolli SIgnoreMisspelledWords e NSChangeSpelling supportano meccanismi di
controllo ortografico.
3.3.2.9 Interface Builder Support
La classe astratta NSNibConnector e le sue sottoclassi, NSNibControlConnector e
NSNibOutletConnector rappresentano le connessioni in Interface Builder,
NSNibControlConnector gestisce un collegamento in Interface Builder e
NSNibOutletConnector gestisce una richiesta di connessione.
3.3.3 UIKit (iPhone OS)
Il framework, UIKit in iPhone OS è il framework gemello di Application Kit in Mac OS
X. Sono essenzialmente uguali: fornisce tutte le classi che un'applicazione necessita per
costruire e gestire le interfacce. Comunque bisogna sottolineare che ci sono delle
differenze su come vengono realizzati questi obiettivi.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
75
Una delle principali differenze è che, in iPhone OS, gli oggetti che appaiono sulla
interfaccia delle applicazioni Cocoa hanno caratteristiche e funzionamenti diversi rispetto
alle applicazioni Cocoa eseguite su Mac OS X, alcuni esempi tipici sono view del testo,
tabelle, e pulsanti, in più i modelli per la manipolazione delle immagini e dei testi sono
significativamente differenti nelle due piattaforme.
Le sezioni che seguono espliciteranno le differenze. E' possibile aggiungere oggetti UIKit
all'interfaccia di utente dell'applicazione in tre modi diversi:
• Usare Interface Builder per sviluppare l'applicazione, con finestre drag and drop, view
ed altre librerie di oggetti.
• Creare, posizionare e configurare oggetti programmando in un framework.
• Implementare oggetti di interfaccia utente personalizzate attraverso sottoclassi di
UIView o classi che discendono da essa.
3.3.3.1 Descrizione delle classi di UIKit
Come per Application Kit, le classi di UIKit sono ereditate da NSObject, la figura 12
rappresenta le relazioni e le gerarchie delle classi di UIKit. Così come Application Kit,
anche UIKit ha la classe Responder come radice del ramo principale. UIResponder
definisce anche le interfacce ed i comportamenti di default per metodi event-handling e
per le catene di responder, i quali sono una catena di oggetti, potenziali gestori di eventi.
Quando un utente scorre una view di una tabella con un dito o clicca su di un carattere in
una tastiera virtuale, UIKit genera un evento che passato alla catena di responder fino ad
un risponditore automatico dell'oggetto che lo gestisce.
Tutti i corrispondenti oggetti fondamentali, UIApplication, UIWindow, UIView
direttamente o indirettamente derivano da UIResponder.
Differentemente da Application Kit, UIKit non usa celle. I controlli, che sono gli oggetti
ereditati da UIControl, non richiedono celle per svolgere il loro ruolo primario: inviare
messaggi di azioni ad oggetti. Eppure il sistema con cui UIKit implementa il meccanismo
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
76
di “azione” è diverso dal modello di Application Kit. La classe UIControl definisce un
gruppo di tipi di eventi per il controllo, per esempio un pulsante (UIButton) che invia un
messaggio di azione ad un oggetto target, è possibile invocare un metodo per associare
l'azione e la destinazione con uno o più tipologie di controllo degli eventi. Quando uno di
questi eventi si verifica, il controllo invia il messaggio d'azione.
Il framework UIKit fa largo uso della delega, tuttavia, l'attuazione della delega è diversa
rispetto ad Application Kit. Invece di usare protocolli informali, UIKit utilizza protocolli
formali, eventualmente con alcuni metodi definiti “opzionale”.
3.3.3.2 Application Coordination
Tutte le applicazioni eseguite su iPhone OS sono gestite da una singola applicazione
oggetto, che ha un ruolo molto simile a quello dell'oggetto NSApplication. un oggetto
UIApplication controlla il principale evento loop, tiene traccia delle finestre e view delle
applicazioni, e distribuisce gli eventi in arrivo agli appropriati oggetti risponditori.
L'oggetto UIApplication riceve anche le notifiche al livello di sistema ed eventi al livello
delle applicazioni. Molte di queste sono passate al suo delegato, che consente di iniettare
alle applicazioni specifici comportamenti quando l'applicazione si avvia o si chiude e
rispondere ai warning.
3.3.3.3 Differenze di modelli nei disegni e negli eventi
In Mac OS X, il mouse e la tastiera generano diversi eventi utente, Application Kit utilizza
oggetti NSEvent per incapsulare questi eventi. Su iPhone OS, tuttavia, i movimenti del
dito sullo schermo sono la causa di eventi, UIKit rappresenta tali eventi con la classe
UIEvent. Il contatto del dito però ha diversi scopi, per esempio il click del mouse oppure
due o più tocchi che si verificano nel corso di un intervallo temporale potrebbero essere un
“pizzico”. Così un oggetto UIEvent contiene uno o più oggetti che rappresentano il tocco
di un dito (UITouch). Il modello per la distribuzione e dispacciamento di eventi agli
oggetti che possono gestirli è quasi identico sulle due piattaforme, tuttavia, per gestire un
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
77
evento, un oggetto deve prendere in considerazione la sequenza di tocchi per quello
specifico evento.
I modelli di disegno sono molto simili per Application Kit e UIKit. In UIKit, tuttavia, un
oggetto UIView ha uno strato di animazione ad esso associato per consentire l'animazione
dei cambiamenti all'interno delle view e nelle transizioni tra diverse view. Detto questo, il
livello di sviluppo di codice che prevede UIKit per il disegno è limitato. Pertanto, per
disegni articolati, un'applicazione deve utilizzare Core Graphics oppure OpenGLES.
3.3.3.4 Classi generali dell'interfaccia utente
Gli oggetti dell'interfaccia utente in iPhone OS sono visibilmente diversi da quelli
dell'interfaccia di Mac OS X. Tali differenze sono dovute ad uno schermo più piccolo ed
all'uso delle dita in luogo del mouse e della tastiera come input dell'utente. Gli oggetti
quindi dell'interfaccia iPhone devono essere sostanzialmente più grandi (per permettere
l'uso del touch) allo stesso tempo però bisogna ottimizzare lo spazio usando lo schermo
nel modo più esteso possibile.
Questi oggetti sono a volte diversi dagli oggetti analoghi in Mac OS X perchè devono
basarsi su concetti visivi ma anche tattili, per esempio consideriamo un oggetto picker per
le date, istanziato da una classe definita sia in Application Kit che in UIKit. In Mac OS Xil
picker avrà questo aspetto:
L'interfaccia del picker ha delle piccole aree per l'incremento dei componenti date, quindi
adatto al puntatore del mouse, ma contrasta con le necessità di iPhone che quindi avrà
questo tipo di interfaccia:
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
78
Questo picker, infatti, è molto più comodo per input ricevuti da touch. Gli utenti possono
scorrere giorno, mese ed anno per selezionare agevolmente quello giusto.
Anche in UIKit molte classi si possono riassumere in gruppi funzionali:
• Controlli. Gli oggetti instanziati delle sottoclassi di UIControl consentono agli utenti di
inviare input alle applicazioni Oltre agli oggetti standard (UIButton) ed agli oggetti a
scorrimento (UISlider) ci sono controlli che simulano switch off/on (UISwitch),
controlli a ruote girevoli per la selezione di gruppi multidimensionali di valori
(UIPickerView), un controllo attraverso il paging dei documenti (UIPageControl) ed
altri controlli.
• Modal views. Le due classi ereditate da UIModalView servono per la visualizzazione
di messaggi all'utente sia sotto forma di fogli allegati, speciali view o finestre
(UIActionSheet) o come messaggi di alert (UIAlertView).
• Scroll views. La classe UIScrollView abilita le istanze delle sue sottoclassi a rispondere
al touch per scrolling all'interno di view più grandi. Quando un utente interagisce con
uno scroll, visualizza uno scorrimento degli indicatori di posizione nella view del
documento. Le sottoclassi di UIScrollView implementano view di tabelle, testo e web.
• Toolbars, barre di navigazione, e controller view. La classe UIViewController è la
classe base per la manipolazione di view. Un controller di view fornisce metodi per
creare, ruotare, osservare e manipolare view e risponde ai warning della memoria.
UIKit include sottoclassi di UIViewController per gestire toolbar, barre di navigazione
e picker.
Le applicazioni utilizzano sia le toolbar che le barre di navigazione per gestire il
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
79
comportamento legato alla view principale dello schermo, in genere le toolbar sono
posizionate al di sotto della view e le barre di navigazione sopra. Gli oggetti toolbar,
UIToolbar, vengono utilizzati per passare tra modelli di view di un'applicazione, è
possibile usarli per visualizzare un set di funzioni che migliorano alcune azioni relazionate
alla view principale. Le barre di navigazione, UINavigationBar, gestiscono sequenze di
finestre o view in un'applicazione e per scorrere una gerarchia di oggetti definiti da
un'applicazione, per esempio, in Mail si usano le barre di navigazione per scorrere tra le
cartelle dell'account di posta o trai messaggi. È da notare come questo approccio differente
tra toolbar e barre di navigazione sia utile per mantenere coerenza nell'utilizzo di differenti
applicazioni.
3.3.3.5 Testo ed immagini
Gli utenti possono inviare testo alle applicazioni iPhone attraverso view di testo
(UITextView) oppure attraverso campi di testo editabili (UITextField). Queste classi
adottano il protocollo UIInputTraits per specificare l'aspetto e le funzionalità di una
tastiera virtuale che si presenta all'utente quando tocca un oggetto di input testo, ogni
sottoclasse che abilita un input di testo dovrebbe essere conforme a questo protocollo. Le
applicazioni possono visualizzare testo usando metodi UIStringDrawing, una categoria
della classe NSString. Inoltre, con la classe UIFont è possibile specificare le caratteristiche
dei font del testo in tutti gli oggetti che visualizzano testo, incluso celle di tabelle, barre di
navigazione ed etichette. UIKit utilizza oggetti UIImage per rappresentare ed incapsulare
immagini.
3.3.4 Comparazione tra classi di Application Kit e UIKit
Application Kit e UIKit sono framework per applicazioni Cocoa destinati a differenti
piattaforme: Mac OS X e iPhone OS. Considerando le affinità tra le piattaforme è
comprensibile che molte classi hanno nomi simili nei due framework, in molti casi il
prefisso, che cambia da NS a UI, è l'unica differenza. In molti altri casi, invece, il nome
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
80
simile delle classi indica che hanno molte funzionalità in comune, ma rimangono
comunque delle differenze che possono riguardare i propositi, le gerarchie o il design. In
generale, le classi UIKit hanno un numero minore di metodi che la loro controparte in
Application Kit, questo perchè le applicazioni iPhone sono eseguite da un ambiente più
ristretto.
La figura 13 visualizza le classi UIKit con le corrispondenti classi in Application Kit, in
tabella invece sono descritte le differenze tra le maggiori classi dei due framework.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
81
Figura 12: Comparazione tra classi Application Kit ed UIKit
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
82
Classi Comparazione
NSApplication
UIApplication
Queste due classi sono molto simili nel loro ruolo principale.
Forniscono un singolo oggetto che stabilisce l'ambiente di
visualizzazione dell'applicazione e l'evento principale loop,
distribuisce gli eventi e notifica un delegato quando lo specifica un
evento dell'applicazione. Comunque NSApplication gestisce eventi
che in iPhone non sono abilitati, come sospensione, riattivazione ed
hiding.
NSResponder
UIResponder
Anche queste classi sono molto simili, sono classi astratte che
definiscono un'interfaccia per rispondere ad eventi e gestire le
catene di risponditori. La principale differenza è che in NSRsponder
i metodi per la manipolazione sono definiti per mouse e tastiera, in
UIResponder sono definite per modelli di eventi multitouch.
NSWindow
UIWindow
La classe UIWindow si trova in un posto differente nella gerarchia
rispetto a NSWindows, è una sottoclasse di UIView, dal momento
che le classi di Application Kit discendono direttamente da
NSResponder. UIWindows inoltre ha funzionalità molto più limitati
in un'applicazione rispetto a NSWindows e fornisce un'area per le
view, smista gli eventi nelle view e converte tra le finestre e le
coordinate view.
NSView
UIView
Queste classi sono molto simili sia negli scopi che nei loro set di
metodi base. Permettono di spostare e ridimensionare le view,
gestire le loro gerarchie, spostare il loro contenuto e convertirne le
coordinate. Tuttavia UIView permette agli oggetti di avere capacità
intrinseche di animazioni.
NSContro
UIControl
Entrambe le classi forniscono un meccanismo per oggetti come
pulsanti o cursori che, se manipolati, permette all'oggetto di
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
83
controllo di inviare un messaggio di azione all'oggetto destinazione.
Le classi implementano questo meccanismo di obiettivo-azione in
differenti modi, in gran parte a causa della differenza tra i modelli di
evento.
NSViewController
UIViewController
Lo scopo di entrambe le classi è gestire le view, è diverso però il
modo in cui si ottiene. La gestione offerta da un oggetto
NSViewController dipende dal bindings che è una tecnologia
specifica di Mac OS X. Gli oggetti UIViewController sono usati nei
modelli delle applicazioni in iPhone OS per la navigazione
nell'interfaccia utente, ad esempio per il controllo della view
attraverso la barra di navigazione.
NSTableView
UITableView
NSTableView discende da NSControl, ma UITableView non
discende da UIControl, la differenza sostanziale è che gli oggetti
NSTableView supportano più colonne di dati, mentre gli oggetti
UITableView ne supportano una sola e quindi funziona più come
una lista che come una tabella di dati.
Anche tra le classi minori si possono trovare differenze, per esempio UIKit ha le classi
UITextField e UILabel per i campi di testo editabili e per quelli non editabili, quindi in
forma di etichette. Con la classe NSTextField è possibile creare entrambi i tipi di oggetti
semplicemente settando text-field come attributo. Similmente, la classe
NSProgressIndicator può creare oggetti che corrispondono alle istanze di
UIProgressIndicator e UIProgressBar.
3.4 Pubblicazione di Software
Per distribuire un'applicazione iPhone bisogna aver aderito al programma iPhone
Developer Program. L'adesione infatti prevede la possibilità di scaricare gratuitamente il
software necessario allo sviluppo e successivamente permette di accedere al App Store.
Nel seguito saranno descritti i vari tipi di adesione al programma di sviluppo applicazioni
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
84
della Apple.
Per sviluppare e distribuire quindi un'applicazione per iPhone la Apple prevede le seguenti
fasi:
• Sviluppare il software con gli strumenti messi a disposizione da Apple e descritti nei
paragrafi precedenti.
• Testare il software prima su iPhone Simulator e successivamente sul dispositivo. Se
necessario è possibile ottimizzare le prestazioni del software sempre attraverso gli
strumenti offerti da Apple.
• Distribuire l'applicazione attraverso l'App Store, sia in maniera gratuita che a fini
commerciali.
3.4.1 iPhone Developer Program
Iphone Developer Program offre un processo completo ed integrato per lo sviluppo di
software, il debugging e la distribuzione di applicazioni per iPhone ed iPod Touch.
Esistono due tipi di accesso al programma di sviluppo per iPhone:
1. Standard Program per sviluppatori che voglio produrre applicazioni commerciali o
gratuite da distribuire sull'App Store. Tale programma ha il costo di 99 $.
2. Enterprise Program è invece rivolto alle aziende con più di 500 dipendenti che
vogliano sviluppare e distribuire al loro interno applicazioni per iPhone ed iPod Touch,
il suo costo è di 400$.
È da notare comunque l'esistenza di un altro programma di sviluppo, iPhone Developer
University Program, rivolto però esclusivamente agli istituti di istruzione superiore negli
Stati Uniti.
Il kit di sviluppo è scaricabile dal sito della Apple senza la necessità di sottoscrivere i
programmi a pagamento. Esiste infatti la possibilità di registrarsi gratuitamente per
scaricare L'SDK per iPhone semplicemente creando un account Apple id. Tale account
permette l'accesso alle sezioni iPhone Dev Center dove è possibile trovare tutta la
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
85
documentazione messa a disposizione da Apple per lo sviluppo di software. Inoltre è
possibile accedere alla sezione Web Apps Dev Center, per lo sviluppo di siti ottimizzati
per iPhone.
Bisogna inoltre specificare che l'adesione gratuita al programma non permette di installare
sul proprio dispositivo le applicazioni sviluppate, neanche a scopo di test. Prima di poterlo
fare infatti è necessaria l'adesione ai programmi descritti precedentemente.
Il vincolo per l'installazione è la firma digitale apposta da Apple una volta visionato il
software, verificati eventuali procedure pericolose ed approvato il progetto. Solo dopo
infatti è possibile distribuirlo tramite App Store.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
86
Capitolo 4 Linguaggio di programmazione Objective-C Il capitolo che segue descrive le caratteristiche degli oggetti in Objective-C e quali
vantaggi portano nello sviluppo di software, inoltre sarà descritto come usare Objective-C
per inviare messaggi ad oggetti e come gestire i valori di ritorno. Il capitolo inizierà con
un piccolo esempio di codice per spiegare la struttura di un semplice programma, seguirà
poi con la descrizione delle funzionalità della classe root, NSObject, e spiegherà come
utilizzare la sua interfaccia di programmazione per creare oggetti, per fornire Introspection
e gestire i cicli di vita degli oggetti stessi.
4.1 Cenni storici Objective-C
Objective-C, è un linguaggio di programmazione orientato agli oggetti, sviluppato da Brad
Cox alla metà degli anni '80 presso la Stepstone Corporation. È possibile definire
Objective-C come un piccolo ma potente set di estensioni per lo standard ANSI C.
Objective-C è nato per restituire al C un approccio alla programmazione orientata ad
oggetti.
Le estensioni a oggetti con cui Objective-C arricchisce il modello semantico del C sono
ispirate al linguaggio Smalltalk, in particolar modo alla gestione dei messaggi. In
Objective-C sono presenti molti concetti innovativi oltre a supportare tutti gli elementi
classici della programmazione a oggetti e mantenere piena compatibilità con il linguaggio
C. La diffusione è stata legata al framework, predecessore di Cocoa, OpenStep di NeXT,
società fondata da Steve Jobs nel 1985.
Le caratteristiche del sistema runtime invece, collocano l'Objective-C tra i linguaggi ad
oggetti dinamici. Fornisce infatti supporto runtime per la distribuzione dei messaggi e
specifica convenzioni sintattiche per definire nuove classi, fornisce anche supporto per
diversi meccanismi presenti in altri linguaggi orientati agli oggetti come C++ e Java, come
gerarchie, incapsulamento, riusabilità e polimorfismo. Ciò nonostante, Objective-C è
diverso dagli altri linguaggi orientati agli oggetti, spesso in caratteristiche essenziali.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
87
Diversamente da C++ infatti non supporta il sovraccarico di operatori, template o
gerarchie multiple. Tali carenze, comunque sono ben bilanciate dai suoi punti di forza.
4.2 Programma di esempio “Hello, World!”
Per chiarire meglio la struttura del linguaggio si inizierà con un semplice programma da
linea di comando che mostra a video una scritta, “Hello, World!”. Si consideri prima il suo
codice ed in seguito si analizzeranno le sue parti:
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
88
/* Commenti:
** Il codice che segue è un esempio di
**programma che restituisce a video la scritto “Hello World”
*/
#include <objc/Object.h>
@interface Greeter:Object
{
/* Tale spazio è lasciato vuoto, ma
** in generale in questa sezione
** vengono dichiarate istanze di variabili.
*/
}
- (void)greet;
@end
#include <stdio.h>
@implementation Greeter
- (void)greet
{
printf("Hello, World!\n");
}
@end
#include <stdlib.h>
int main(void)
{
id myGreeter;
myGreeter=[Greeter new];
[myGreeter greet];
[myGreeter free];
return EXIT_SUCCESS;
}
Riquadro 1: Hallo World
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
89
Il comando per eseguire e visualizzare l'output del programma è indicato nel Riquadro 2. bash-2.05a$ ./hello
Hello, World!
Riquadro 2: Output
Per facilitare il lavoro del compilatore è necessario fare un corretto uso delle estensioni dei
file, la seguente tabella indica le estensioni di Objective-C, C e C++ e le corrispondenti
direttive per GCC.
Linguaggi Estensioni Direttive
C .c -x c
Objective-
C
.m -x objective-c
C++ .c o .c++ -x c++
Tabella 1: Estensioni dei file
Ogni programma è formato da funzioni e variabili, le funzioni descrivono cosa fa il
programma e le variabili contengono i dati che le funzioni elaborano. In linguaggi orientati
ad oggetti, le variabili e le funzioni fanno entrambe parte degli oggetti. Objective-C
gestisce la comunicazione tra gli oggetti attraverso lo scambio di messaggi, attraverso di
essi si invocano le funzioni o scambiano parametri.
Un oggetto consiste in una variabile istanziata (dato) che rappresenta lo stato di un
oggetto, e metodi (funzioni) che agiscono sulle variabili in risposta a messaggi. Un
oggetto quindi può essere visto come un semplice programma da chiamare (inviandogli un
messaggio) per eseguire una semplice azione.
Molto spesso si utilizzano oggetti già descritti in altri file, per inserirli nel programma da
implementare si utilizza la direttiva #include, indicando così al compilatore di andare a
prelevare l'oggetto segnalato. Nell'esempio si è incluso l'oggetto Object.h, fornito dal
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
90
compilatore.
Gli oggetti definiti in Object.h sono la base di tutti gli oggetti, per questo motivo si
considera Object come la classe radice di tutte le classi.
4.2.1 Oggetti, Classi, metodi ed Istanze
Un programma orientato ad oggetti è generalmente costituito da una diversità di oggetti
che interagiscono tra loro. In Objective-C si dichiara un oggetto definendo la sua classe.
La definizione della classe quindi è il prototipo per un determinato tipo di oggetto,
dichiara infatti le variabili di istanza che fanno parte di ogni membro della classe e
definisce un set di metodi che tutti gli oggetti della classe possono usare. Il compilatore
crea un solo oggetto accessibile per ogni classe, detto class object. Tale oggetto ha il
compito di generare nuovi oggetti appartenenti alla classe. In sostanza class object è la
versione compilata della classe, gli oggetti che crea sono le istanze della classe. Gli oggetti
del programma sono le istanze create dal class object al tempo di esecuzione. Inoltre è
importante notare come gli oggetti creati da una classe ereditino tutti i metodi delle
superclassi della classe da cui derivano.
La dichiarazione delle interfacce di classe avviene attraverso la struttura
@interface/@end, tale sezione normalmente è salvata in un file con estensione .h, anche
se nell'esempio del Riquadro 1, per motivi di semplicità, è stato inserito nel file principale.
Nel seguente riquadro è indicato lo stralcio di codice dell'esempio incluso nella struttura
@interface/@end.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
91
@interface Greeter:Object
{
//è la sezione destinata alla dichiarazione
//delle veriabili istanziate
}
- (void)greet;
@end
Riquadro 3: Struttura @interface/@end
La prima linea indica al compilatore che Greeter è una sottoclasse di Object, ereditandone
così i metodi e posizionando la nuova classe nella gerarchia delle classi. In tal modo un
oggetto Greeter ha le stesse funzionalità di un oggetto Object. All'interno delle parentesi
graffe invece vengono dichiarate le variabili di istanza, quindi la struttura dati di ogni
istanza della classe.
Dopo le parentesi graffe e prima della direttiva @end vengono dichiarati i metodi pubblici
associati alla classe. In tal modo con la dichiarazione “- (void)greet;”, Greeter può godere
anche del nuovo metodo greet. I metodi sono funzionalità che una classe può offrire e ne
esistono di due tipi: quelli ereditati dalle classi da cui discende e quelli aggiunti. Tra i
metodi aggiunti va notato che sono pubblici tutti quelli richiamati nell'interfaccia della
classe a cui appartengono (file con estensione .h); sono invece privati quei metodi
implementati direttamente nella descrizione della classe (file con estensione .m). Un
metodo che può essere usato da una istanza della classe viene preceduto dal segno “-”, un
metodo di questo tipo quindi può essere usato solo in presenza di una istanza. Un metodo
che invece viene usato indipendentemente dalle istanze dalla classe viene preceduto dal
segno “+” e chiamato “metodo di classe”.
All'interno delle parentesi tonde che precedono il nome del metodo è definito il tipo
restituito dal metodo stesso. Nel caso in cui non venga specificato si considera restituito
un tipo generico id. Gli argomenti del metodo invece vengono dichiarati dopo i due punti,
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
92
come nel seguente esempio:
- (void)greet:(float)agreat;
così facendo il metodo greet ha come argomento agreat di tipo float. Se gli argomenti
sono molteplici invece, questi vengono aggiunti in sequenza come nel seguente esempio:
- (void)greet:(float)agreat minus(int)aminus;
la definizione dell'interfaccia di una classe viene conclusa con la direttiva @end.
È possibile inoltre estendere le funzionalità delle classi, tali estensioni sono infatti come
categorie anonime. I metodi che dichiarano però devono essere implementati nel blocco
@implementation del main per la classe corrispondente. È utile notare che il compilatore
non può verificare che tutti i metodi siano stati implementati.
4.2.2 Ereditarietà
Dall'esempio è intuibile quanto sia importante la funzionalità di ereditarietà di Objective-
C, in quanto con una sola riga di codice abbiamo incluso nella nostra classe tutti i metodi e
dati presi dalla classe Object. Questo però non rende personalizzata la classe Greeter, in
quanto è identica alla classe Object, Quello che la differenzia invece è l'aggiunta del
metodo greet attraverso la procedura - (void) greet, con la quale si è indicato al
compilatore l'aggiunta di un metodo greet associata ad ogni oggetto Greeter. È possibile
quindi considerare le definizioni di classe come additive, in quanto dichiarare una classe
significa aggiungere metodi alla classe da cui deriva.
4.2.3 Implementazione
Successivamente alla definizione delle classi si passa alla fase di implementazione in cui
si descrive cosa l'oggetto Greeter deve fare. Poiché si sono fatti ereditare al nostro oggetto
tutti i metodi della classe Object, non si deve implementare nulla se non quelle funzioni
che personalizzano l'oggetto greeter.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
93
#include <stdio.h>
@implementation Greeter
- (void)greet
{
printf("Hello, World!\n");
}
@end
Riquadro 4: Implementazione 1
La sezione indicata in Riquadro 4 indica cosa il programma deve fare, in particolare deve
visualizzare a video le parole Hello, World!.
La prima notazione da fare nel Riquadro 4 è la presenza di un'altra direttiva #include.
Generalmente infatti le direttive #include vengono tutte raggruppate all'inizio del
programma, ma in questo caso è inclusa nella sua sezione di riferimento, in modo da
semplificare la spiegazione.
In conclusione bisogna considerare la sezione che definisce cosa il programma deve fare.
Ogni programma ha una funzione speciale detta main. In genere è possibile chiamare in
qualunque modo le funzioni implementate, l'importante è non chiamarle main, attraverso
tale funzione infatti si indica al compilatore il punto di accesso al codice eseguibile.
Si analizzi quindi la funzione main descritta nel Riquadro 5:
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
94
#include <stdlib.h>
int main(void)
{
id myGreeter;
myGreeter=[Greeter new];
[myGreeter greet];
[myGreeter free];
return EXIT_SUCCESS;
}
Riquadro 5: Funzione main
Notazione importante è l'uso delle parentesi graffe, { e }, chiamate blocco delle
dichiarazioni. In genere ogni funzione ha un proprio blocco di dichiarazioni. Nell'esempio
indicato nel Riquadro 5 la funzione main ha 5 dichiarazioni che si concludono con il
simbolo “;”. Inoltre è possibile superare il limite delle singole dichiarazioni
raggruppandole, ma sempre all'interno delle parentesi graffe.
Innanzitutto viene dichiarata una variabile, (un oggetto) chiamata myGreeter.
id myGreeter;
Successivamente si crea l'oggetto della classe Greeter ed una nuova istanza inviando il
messaggio new a Greeter ed associandolo all'oggetto di tipo id14 myGreeter.
myGreeter = [Greeter new];
14 Nella dichiarazione viene utilizzato un tipo di variabile id, definito in Objective-C come un puntatore ad un oggetto
generico. L'utilizzo di un tipo id permette di non specificare al momento della dichiarazione il tipo di oggetto
myGreeter (vedi il paragrafo 4.3.2 Dinamicità in Objective-C).
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
95
4.2.4 Messaggi e polimorfismo
Inviando un messaggio ad un oggetto è possibile indicare che metodo eseguire. Nell'esempio
considerato il messaggio indica all'oggetto Greeter di eseguire il metodo new, uno dei metodi
ereditati dalla classe Object.
Un'altro possibile modo di creare un oggetto è nel seguente modo:
myGreeter = [[Greeter alloc] init];
Tale metodo è spesso utilizzato nella programmazione Cocoa e GNUstep, è possibile
quindi inviare due messaggi nidificati ad un oggetto.
Ottenuto un oggetto funzionante, è possibile inviagli altri messaggi per implementare
metodi, nel caso dell'esempio il metodo greet.
Una volta che l'oggetto ha svolto il suo compito è necessario rilasciarlo (con il messaggio
release) in quanto con la sua creazione è stata occupata memoria che potrebbe servire per
la creazione di altri oggetti.
La funzione finisce quando ritorna un valore EXIT_SUCCESS che indica la corretta
chiusura della funzione.
Si osservino ora alcune caratteristiche che riguardano i messaggi scambiati. I messaggi in
Objective-C appaiono nella stessa posizione sintattica delle chiamate di funzioni in
standard C, ma poiché i metodi appartengono ad un oggetto, i messaggi si comportano in
maniera diversa rispetto alle funzioni. In particolare un oggetto può essere utilizzato
soltanto da alcuni metodi che gli sono stati associati, non è possibile quindi mischiare i
metodi tra oggetti anche se hanno lo stesso nome. Tale approccio permette ad oggetti
diversi di rispondere allo stesso messaggio in modo diverso, ad esempio un cerchio o un
rettangolo possono rispondere in maniera diversa ad istruzioni identiche. Questa
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
96
caratteristiche, definita polimorfismo, svolge un ruolo significativo nella progettazione di
programmi Objective-C e sarà affrontata nella trattazione dei paragrafi successivi in tutte
le sue caratteristiche. Insieme alla caratteristica di dinamicità, in particolare il binding
dinamico (vedi paragrafo 4.3.2 Dinamicità in Objective-C) è possibile scrivere codice che
potrebbe essere applicato ad un numero qualsiasi di oggetti, senza dover scegliere al
momento dell'implementazione. È possibile infatti implementare un metodo che invia un
messaggio ad un oggetto id, potenzialmente quindi tutti gli oggetti possono ricevere tale
messaggio.
4.2.5 Scomporre il codice in più file
Objective-C, esattamente come il C, prevede la possibilità di strutturare un programma in
più file. È possibile quindi descrivere l'interfaccia delle classi in file con estensione “.h” e
successivamente implementarle in file con estensione “.m”.
Si noti che strutturare il programma in file che distinguono le diverse classi favorisce da
un lato la leggibilità del codice, dall'altro il riutilizzo di classi già implementate.
4.2.6 Elementi comuni al C
Come si è descritto in precedenza, Objective-C è un'estensione del linguaggio C, nel
seguito quindi si continuerà con la descrizione di quegli elementi C che compongono
oggetti e funzioni.
4.2.6.1 Tipi di dato
I “tipi” di dato indicano l'insieme dei valori che una variabile (o il risultato di
un'espressione) può assumere e le operazioni possibili su di essa. I linguaggi di
programmazione possono godere di tipizzazione dinamica o statica. La differenza risiede
nel fatto che in una tipizzazione statica una variabile non può cambiare il valore
memorizzato fino alla compilazione, deve quindi conservare un dato del tipo per cui la
variabile è stata inizializzata.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
97
Objective-C è un linguaggio che gode della tipizzazione dinamica15. La loro differenza è
nell'assegnazione dei valori dati alle variabili. Una variabile è infatti come un contenitore
di valori, per esempio scrivere VAR=1 significa che la variabile chiamata VAR avrà 1
come valore, questo tipo di assegnazione è dinamica in quanto non si era specificato che
VAR potesse contenere valori interi. In un linguaggio a tipizzazione dinamica quindi è
possibile assegnare VAR=a. È possibile quindi in Objective-C creare un oggetto e soltanto
al tempo di esecuzione assegnargli un valore di un determinato tipo. Una tale procedura
non genererebbe infatti errore al tempo di compilazione.
Il linguaggio C invece è staticamente tipizzato. Bisogna quindi dichiarare che tipo di dato
una variabile può rappresentare. Per fare questo C fornisce tipi di dati predefiniti con i
quali vengono inizializzate le variabili.
I “tipi” servono a dare al compilatore indicazioni sul tipo di dato da memorizzare e la
memoria che utilizzano per registrare tali informazioni. Tipo di
Dato Descrizione Dimensione
Char Un singolo carattere del set di caratteri locale 1 byte
Int Un numero intero (1, 4, 300) 4 byte
Float Un numero in virgola mobile (1.2, 4.78) 4 byte
Riquadro 6: Tipi standard in C
char. È l'unità base in un programma C oppure Objective-C, tutte le altre derivano dal
char. Rappresenta un singolo carattere come 'a' oppure '3', la sua dimensione è definita in
limitis.h.
15 Vedi paragrafo 4.3.2 Dinamicità in Objective-C.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
98
Tipo di Dato Descrizione Dimensione
Short Intero corto 2 byte
Long Un intero doppio corto 4 byte
Long long Un intero doppio lungo 8 byte
Double Float a doppia precisione 8 byte
Riquadro 7: Tipi derivati
Tutti i tipi menzionati sono comuni alle macchine a 32 bit, come Intel Pentium e sistemi
compatibili. La lunghezza delle variabili è dipendente dalla macchina ed è possibile
trovarla nei file limits.h e float.h.
Objective-C, inoltre, introduce i tipi di dato id, come introdotto nell'esempio “Hello
World” inizializzando un oggetto myGreeter. A tale oggetto successivamente si è associato
infatti una istanza della classe Greeter con tutti i suoi metodi.
4.2.6.2 Funzioni
Dopo aver analizzato i dati ed i tipi di dato bisogna valutare le routine base che li
manipolano, queste sono le funzioni. Tutti gli oggetti (in un linguaggio orientato ad
oggetti) consistono in dati e funzioni, che verranno descritte nel seguito.
Nel Riquadro 8 è indicata la struttura di una funzione in linguaggio C.
return-type function-name(parameter declaration, if any)
{
declarations;
asignments;
statements;
}
Riquadro 8: Funzione in C
Ogni funzione può essere chiamata in qualunque modo fuorchè main, perchè, come già
accennato, per il compilatore la funzione main ha il valore di funzione principale. Si
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
99
considera inoltre che ogni funzione ha un valore di ritorno e può avere dei parametri. Alla
sua conclusione, una funzione deve restituire un parametro, ad esempio per indicare che
ha avuto una conclusione positiva potrebbe restituire uno zero. Il parametro restituito
potrebbe altresì essere una dichiarazione o qualunque altro tipo di dato analizzato
precedentemente.
Per i parametri dichiarati è possibile fare le stesse affermazioni. I parametri dichiarati
descrivono i dati che la funzione si aspetta. Nel caso dell'esempio 'Hallo, World!' la
funzione main è definita come void il che vuol dire che non si aspetta nessun input.
Nella funzione main quindi il tipo di ritorno dalla funzione è un int e non vi sono
dichiarazioni di parametro, ciò significa che la funzione non accetta alcun argomento, non
è possibile quindi inviare dati a questa funzione dal momento che si definisce void.
Il corpo della funzione è tra parentesi graffe { e }, che contengono quindi tutto ciò che
deve elaborare la funzione main.
Come è mostrato nell'esempio Hello, World! È intuibile quanto una funzione potrebbe
dipendere da una libreria esterna. In realtà molte funzioni dipendono da librerie esterne,
per questo infatti i linguaggi C ed Objective-C risultano molto compatti.
4.2.6.3 Costrutti
Objective-C prevede la possibilità di valutare se un oggetto è true o false, consentendo
così la possibilità di creare cicli while e for. Inoltre è previsto l'uso del costrutto if … else
e delle abbreviazioni dei valori incrementali come: Incrementi Abbreviazione
s=s+1; s++;
s=s-1; s--;
s=s+5; s+=5;
s=s*j; s*=j;
L'implementazione di cicli e costrutti è identica a quanto avviene nel linguaggio C.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
100
4.3 Objective-C e oggetti Cocoa
Nel paragrafo precedente si è studiato un semplice programma di esempio per descrivere
la struttura base del linguaggio Objective-C. In questo paragrafo invece si affronteranno le
caratteristiche principali di tale linguaggio e lo si osserverà in relazione agli oggetti Cocoa.
Come discusso precedentemente, Cocoa è strutturalmente orientato agli oggetti, dai suoi
paradigmi e meccanismi all'architettura event-driven. Nella trattazione seguente si farà
riferimento a SimpleCocoaTool (Riquadro 9) un programma di esempio da riga di
comando creato usando il framework Foundation per Mac OS X. Tale codice prende come
argomenti una serie arbitraria di parole, il programma rimuove le ripetizioni, le ordina e
restituisce in output la lista in ordine alfabetico delle stesse.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *args = [[NSProcessInfo processInfo] arguments];
NSCountedSet *cset = [[NSCountedSet alloc] initWithArray:args];
NSArray *sorted_args = [[cset allObjects]
sortedArrayUsingSelector:@selector(compare:)];
NSEnumerator *enm = [sorted_args objectEnumerator];
id word;
while (word = [enm nextObject]) {
printf("%s\n", [word UTF8String]);
}
[cset release];
[pool release];
return 0;
}
Riquadro 9: SimpleCocoaTool
Il primo fattore da notare è che il codice appare sintetico, forse molto più dello stesso
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
101
codice scritto in una versione standard di ANSI C e nonostante l'apparenza bisogna dire
che molti degli elementi di questo codice sono simili a quelli ANSI C. Questi elementi
comprendono l'assegnazione degli operatori, il controllo del flusso di dichiarazioni
(while), le chiamate verso le routine delle libreria C (printf) e così via.
L'output di SimpleCocoaTool è:
localhost> SimpleCocoaTool a z c a l q m z
a
c
l
m
q
z
Riquadro 10: Output di SimpleCocoaTool
Molti esempi nei paragrafi seguenti ricalcano il codice appena esposto.
4.3.1 I vantaggi di Objective-C
Programmare una nuova procedura, con un approccio orientato agli oggetti, è facilitato
principalmente pensando ad un oggetto come una struttura con le funzioni ad esso
associate. Questo approccio non è molto lontano dalla realtà, in particolar modo in termini
di realizzazione runtime.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
102
Objective-C è un linguaggio estremamente improntato sullo scambio di messaggi,
caratteristica che restituisce un elevato dinamismo. Quando un oggetto viene creato, la
memoria viene allocata e le sue variabili di istanza inizializzate. Ogni oggetto così creato
nasconde una struttura dati il cui primo membro è il puntatore isa, molti dei membri
restanti sono definiti dalle classi e superclassi dell'oggetto. Il puntatore isa punta alla
classe dell'oggetto (vedi Figura 14) che possiede a sua volta un puntatore alla propria
superclasse ed una tabella di dispatch. Questa tabella contiene i selector che puntano alle
implementazioni dei metodi rappresentati dai selector stessi. Ad esempio il selector per il
metodo setOrigin:: è associato all'indirizzo della procedura che implementa setOrigin::.
Ogni classe quindi è legata attraverso un puntatore alla propria superclasse. Attraverso
questa catena di riferimenti, un oggetto ha accesso a tutti i metodi implementati dalla
propria classe e dalla superclasse. Il puntatore isa è fondamentale per il meccanismo di
distribuzione dei messaggi e per il dinamismo degli oggetti Cocoa.
Questa visione degli oggetti semplifica notevolmente cosa accade nel runtime Objective-C
per la distribuzione dei messaggi, le gerarchie ed altre caratteristiche generali di
comportamento degli oggetti. Inoltre questo tipo di approccio è fondamentale per
comprendere le principali caratteristiche di Objective-C ed il suo dinamismo.
4.3.2 Dinamicità di Objective-C
Objective-C è un linguaggio estremamente dinamico, in quanto sposta molta della
responsabilità di risoluzione simbolica dal tempo di compilazione a tempo di runtime,
Figura 13: Puntatore ISA
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
103
quando il controllo è dell'utente. Objective-C è un linguaggio più dinamico di altri per il
possesso di tre caratteristiche principali: • Tipizzazione dinamica (Dynamic typing), determinando la classe di un oggetto a
runtime
• Invocazione dinamica (Dynamic binding) dei metodi, determinando il metodo da
invocare a runtime
• Caricamento dinamico (Dynamic loading), aggiungendo nuovi moduli ad un software
a runtime
Per la tipizzazione dinamica Objective-C introduce il tipo id, che può rappresentate ogni
tipo di oggetto.
Il tipo id rende possibile la sostituzione di ogni tipo di oggetto a runtime, pertanto è
possibile lasciare ad altri fattori in fase di esecuzione la scelta di quale tipo di oggetto
inserire nel codice. Tale tipizzazione permette associazioni tra gli oggetti da determinare in
fase di esecuzione, piuttosto che forzarli nel codice in una struttura statica. I tipi statici
garantiscono al tempo di compilazione una maggiore integrità dei dati, in cambio di questa
maggiore integrità la tipizzazione dinamica offre maggior flessibilità. Inoltre è possibile
verificare il tipo di oggetto a runtime, determinando la sua idoneità per particolari
operazioni, ovviamente è anche possibile definire gli oggetti staticamente.
La tipizzazione dinamica garantisce robustezza all'invocazione dinamica di metodi. La
seconda caratteristica di dinamicità in Objective-C rimanda così la decisione del metodo
da utilizzare al tempo di esecuzione. L'invocazione del metodo avviene quindi non al
tempo di compilazione ma proprio quando un messaggio è stato recapitato. Con queste
due caratteristiche di dinamicità è possibile quindi ottenere diversi risultati ogni volta che
eseguiamo il codice. Fattori al tempo di esecuzione possono in tal modo determinare sia il
ricevitore che il metodo da invocare.
Quando si invia un messaggio ad un oggetto tipizzato dinamicamente, il sistema runtime
usa il puntatore isa del ricevitore per determinare la classe dell'oggetto e da lì il metodo da
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
104
invocare, il metodo così è legato dinamicamente al messaggio. Nel codice, inoltre, non
bisogna aggiungere nulla per beneficiare della dinamica dei metodi invocati, avviene tutto
in trasparenza nel momento in cui viene inviato un messaggio, maggiormente se l'oggetto
è tipizzato dinamicamente.
In fine, il caricamento dinamico, è la caratteristica Cocoa che dipende da Objective-C per
il supporto runtime. Grazie al caricamento dinamico un programma Cocoa può caricare
codice eseguibile e le risorse necessarie quando ne ha bisogno, senza doverlo fare al
momento del lancio. Il codice eseguibile (collegato prima del caricamento) spesso
contiene nuove classi che vengono integrate nell'immagine runtime del programma. Sia il
codice che le risorse localizzate (incluso il nib file) sono contenute in un bundle e sono
caricate esplicitamente da metodi definiti nella classe NSBundle di Foundation.
Questo tipo di caricamento di codice e risorse migliora le prestazioni diminuendo la
memoria richiesta dal sistema, conseguenza più significativa è che le applicazioni
diventano estensibili. Altra conseguenza è la possibilità di aggiungere un'architettura di
plug in alle applicazioni, permettendo così agli sviluppatori di personalizzare il software
con moduli aggiuntivi che l'applicazione può caricare molto tempo dopo il suo rilascio,
ammettendo però che il design sia coerente e che le classi non entrino in conflitto.
4.3.3 La Classe Root
Il linguaggio Objective-C ed il sistema runtime non sono sufficienti per costruire un
programma orientato agli oggetti, manca una definizione dell'interfaccia comune a tutti gli
oggetti; la classe root fornisce tale definizione.
Una classe root è chiamata così perchè è la radice di una gerarchia di classi, in questo caso
della classe gerarchica Cocoa. La classe root non deriva da nessuna classe e tutte le altre
classi derivano da essa. Gli oggetti Cocoa derivano gran parte delle loro funzionalità dalla
classe root, attraverso questa, Cocoa interagisce con il runtime.
Cocoa fornisce due classi di root, NSObject e NSProxy, quest'ultima classe è definita
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
105
come una superclasse astratta per gli oggetti che agiscono come supporto per altri oggetti,
inoltre NSProxy è essenziale nelle architetture di oggetti distribuiti. Poiché tale ruolo è
molto specializzato risulta poco frequente nei programmi Cocoa, quando infatti si fa
riferimento alla classe root spesso si considera NSObject.
Il paragrafo in oggetto si occupa di NSObject, come interagisce con il runtime, il
comportamento base e le interfacce per tutti gli oggetti Cocoa. Fondamentali sono le
dichiarazioni di metodi per l'assegnazione, l'inizializzazione, l'introspector, la gestione
della memoria ed il supporto al runtime, concetti fondamentali per la comprensione di
Cocoa.
4.3.3.1 NSObject
NSObject non ha una superclasse, da essa ereditano molte classi Objective-C. Tali classi
ereditano l'interfaccia base al sistema runtime per il linguaggio Objective-C e le sue
istanze ottengono le funzionalità tali da diventare oggetti.
Anche se non è strettamente una classe astratta NSObject lo è virtualmente, infatti un
oggetto NSObject non può fare nulla di utile al di là di essere un semplice oggetto. Per
aggiungere gli attributi e le funzionalità del programma specifico è necessario creare una o
più classi ereditate da NSObject o da qualsiasi altra classe derivata.
NSObject adotta il protocollo NSObject che abilita l'uso di oggetti di root. Per esempio
NSProxy non deriva da NSObject, ma adotta il protocollo NSObject così da avere
un'interfaccia comune con altri oggetti Objective-C.
4.3.3.2 Classe root e Protocolli
Si è detto che NSObject è sia il nome di una classe che di un protocollo, entrambi
essenziali per definire un oggetto Cocoa. Il protocollo specifica la base di
programmazione delle interfacce richiesta da tutte le classi root in Cocoa, quindi non solo
la classe NSObject utilizza il protocollo omonimo, ma anche NSProxy, l'altra classe di
root. La classe NSObject fornisce le specifiche per la programmazione delle interfacce per
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
106
tutti gli oggetti Cocoa che non sono oggetti Proxy.
Un protocollo come NSObject è usato nella maggioranza delle definizioni di oggetti
Cocoa, rendendo possibile l'utilizzo di più classi di root, ognuna delle quali condivide una
interfaccia comune come definito dal protocollo adottato.
Da un altro punto di vista NSObject non è semplicemente un protocollo di root, inoltre la
classe NSObject non adotta formalmente i protocolli NSCopying, NSMutableCopying e
NSCoding, ma dichiara ed implementa metodi ad essi relativi. Nell'header file
NSObject.h, che contiene la definizione della classe NSObject, ci sono anche le
definizioni dei quattro protocolli menzionati precedentemente. Copiare, codificare e
decodificare sono operazioni fondamentali per gli oggetti, molte sottoclassi adottano o
sono conformi a tali protocolli.
Le altre classi possono aggiungere metodi ad NSObject attraverso le categorie, queste
spesso sono protocolli informali usati nelle deleghe. Le categorie permettono ai delegati di
scegliere quali metodi delle categorie implementare.
4.3.3.3 Descrizione dei metodi della classe di root
La classe root NSObject, insieme ai protocolli adottati ed altri protocolli di root,
specificano le seguenti caratteristiche di interfaccia e di funzionamento per tutti gli oggetti
Cocoa non proxy: 1 Allocazione, inizializzazione e duplicazione. Alcuni metodi di NSObject (incluso
alcuni dei protocolli adottati) contribuiscono alla creazione, inizializzazione e
duplicazione di oggetti:
a. Alloc ed allocWithZone: sono metodi che allocano memoria per un oggetto da una
zona di memoria e imposta l'oggetto al punto di runtime di definizione della classe.
b. Il metodo init è il prototipo per l'inizializzazione di oggetti, la procedura che
imposta le variabili istanziate di un oggetto ad un noto stato iniziale. Il metodo di
classe initialize e load assegnano alle classi la possibilità di inizializzarsi.
c. New è un metodo che combina allocazione e inizializzazione.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
107
d. Copy e copyWithZone: sono metodi che fanno copie di ogni oggetto membro di una
classe che implementa tali metodi (dal protocollo NSCopying); mutableCopy e
mutableCopyWithZone: (definiti nel protocollo NSMutableCopying) sono
implementati da classi che costruiscono copie modificabili degli oggetti.
2 Acquisizione e rilascio di oggetti. I metodi che seguono sono particolarmente
importanti per un programma orientato ad oggetti che usa formalismi tradizionali ed
espliciti per la gestione della memoria con modalità memory management:
a. Il metodo retain incrementa un contatore retain dell'oggetto.
b. Il metodo release decrementa un contatore retain dell'oggetto.
3 Il metodo autorelease spesso decrementa un contatore retain di un oggetto, ma in modo
differente.
a. Il metodo retainCount restituisce il valore corrente del contatore retain di un
oggetto.
b. Il metodo dealloc è implementato da una classe per rilasciare le sue istanze delle
variabili degli oggetti e liberare la memoria allocata dinamicamente.
4 Introspezione e comparazione. Molti metodi NSObject abilitano richieste runtime su
di un oggetto. Questi metodi introspettivi aiutano a ricercare la posizione di un oggetto
nella gerarchia della classe, determinare quando implementa un determinato metodo, e
verificare se è conforme ad un particolare protocollo. Alcuni di questi sono solo metodi
di classe.
a. I metodi class e superclass (classi ed istanze) restituiscono la classe e la
superclasse del ricevitore come oggetti classe.
b. É possibile determinare il raggruppamento della classe di un oggetto con i metodi
isKindOfClass: ed isMemberOfClass:. Il secondo metodo serve e verificare se il
ricevitore è una istanza di una specifica classe. Il metodo isSubclassOfClass:
invece verifica la gerarchia della classe.
c. Il metodo respondsToSelector: verifica se il ricevitore implementa un metodo
identificato da un selettore. Il metodo di classe instancesRespondToSelector:
verifica se le istanze di una determinata classe attuano il metodo specificato.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
108
d. Il metodo ConformsToProtocol: verifica se un ricevitore è conforme per ricevere
un protocollo.
e. I metodi isEqual: ed hash sono usati nelle comparazioni di oggetti.
f. Il metodo description abilita un oggetto a restituire una stringa che descrive il suo
contenuto. Questo output è spesso usato durante il debugging (comando “print
object”) ed attraverso le specifiche “%@” per oggetti in formato stringa.
5 Codifica e decodifica di oggetti. I metodi che seguono sono pertinenti alla codifica e
decodifica di oggetti, come parte di un processo di archiviazione:
a. I metodi encodeWithCoder: e initWithCoder: sono gli unici membri del protocollo
NSCoding. Il primo abilita un oggetto a codificare le sue variabili istanziate ed il
secondo abilita un oggetto ad inizializzare sé stesso per decodificare le variabili
istanziate.
b. La classe NSObject dichiara altri metodi relativi alla codifica di oggetti:
classForCoder, replacementObjectForCoder: e awakeAfterUsingCoder:
i. Inoltro di messaggi. Il metodo forwardInvocation: e quelli ad esso
relativi permette ad un oggetto di inoltrare un messaggio ad un altro
oggetto.
ii. Distribuzione di oggetti. Un insieme di metodi che iniziano con
performSelector... abilitano la distribuzione di messaggi dopo uno
specificato ritardo ed a distribuire messaggi (sincroni o asincroni) da un
thread secondario al thread principale.
È importante comunque notare che NSObject implementa molti altri metodi per le classi.
4.3.3.4 Metodi di Classe e metodi di istanza
Il sistema runtime gestisce i metodi della classe di root in modo speciale. I metodi di
istanza definiti nella classe root possono essere realizzati sia da istanze che da classi di
oggetti. Pertanto i metodi di istanza della classe root possono essere usati sia dagli oggetti
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
109
di una classe che dalla classe stessa come se fossero metodi di classe. Tale procedura è
possibile a patto che non ci siano metodi con lo stesso nome implementati nella classe che
li invoca.
Ad esempio, una classe potrebbe inviare un messaggio per realizzare i metodi di istanza
respondsToSelector: e performSelector:withObject: di NSObject:
SEL method = @selector(riskAll:);
if ([MyClass respondsToSelector:method])
[MyClass performSelector:method withObject:self];
Si noti che gli unici metodi di istanza disponibili ad una classe sono quelli definiti nella
sua classe root. Nell'esempio riportato, se MyClass avesse sovrascritto
respondsToSelector:, performSelector: e withObject:, queste nuove versioni sarebbero
servite esclusivamente alle proprie istanze, in quanto metodi di istanza. Un oggetto classe
invece avrebbe continuato ad accedere ai metodi di classe definiti da NSObject.
Naturalmente se MyClass avesse implementato respondsToSelector:, performSelector: e
withObject:, come metodi di classe piuttosto che metodi di istanze, allora gli oggetti classe
avrebbero potuto utilizzare queste nuove versioni.
4.3.4 Allocazione e rilascio di oggetti
Objective-C, con il rilascio di Mac OS X 10.5, implementa due metodi per garantire la
persistenza ed il rilascio di oggetti. L'approccio più utilizzato in Mac OS X 10.5 è Garbage
Collection: il sistema di runtime rileva gli oggetti non più necessari e li dispone
automaticamente, tale approccio risulta anche il più semplice nella maggior parte dei casi.
Il secondo approccio, chiamato memory management, si basa sul conteggio dei
riferimenti: ciascuna chiamata che rivendica la proprietà di un oggetto (allocazione,
inizializzazione, copia e trattenuta) deve essere bilanciata da una chiamata che rimuove
tale proprietà (rilascio ed autorilascio). Ogni oggetto ha un contatore che indica il numero
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
110
di richieste che riceve, quando tale contatore decresce fino a 0, l'oggetto è deallocato e la
memoria occupata viene liberata.
Nel seguito del capitolo analizzeremo esclusivamente il metodo memory management, in
quanto Garbage Collection non è implementabile in iPhone OS. Bisogna comunque
sottolineare che Garbage Collection ha una gestione delle memoria più semplice di
memory management, rendendo inoltre semplice l'implementazione di metodi accessori.
Un'altra considerazione importante è che i due paradigmi di gestione della memoria non
sono compatibili, quindi non è raccomandabile migrare un'applicazione gestita con
memory management in un programma che cerca di supportare entrambi i metodi, ma
piuttosto è consigliabile costruire una nuova applicazione che supporti Garbage
Collection.
4.3.4.1 Memory management
Nel codice Objective-C di Memory Management un oggetto Cocoa esiste in più di un
ciclo di vita e potenzialmente ha fasi distinte. Un oggetto quindi è allocato, inizializzato ed
usato. È inoltre possibile trattenerlo, copiarlo o archiviarlo ed eventualmente rilasciarlo e
distruggerlo. Verranno analizzate nel seguito le fasi di vita di un oggetto Cocoa.
Ogni oggetto Cocoa porta con sé un numero intero, che indica il numero di altri oggetti
che sono interessati alla sua persistenza, questo numero è chiamato contatore retain
dell'oggetto. Quando si crea un oggetto o usando metodi costruttori delle classi o metodi di
classi come alloc e allocWithZone:, Cocoa realizza due operazioni molto importanti:
1 Configura il puntatore isa dell'oggetto alla classe dell'oggetto, così facendo l'oggetto
verrà integrato nella vista runtime della gerarchia delle classi.
2 Configura il contatore retain dell'oggetto ad uno, una sorta di variabile nascosta gestita
dal runtime. Si presuppone che il creatore dell'oggetto sia interessato alla sua
persistenza.
Successivamente all'allocazione generalmente si inizializza l'oggetto impostando le istanze
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
111
delle sue variabili ad accettabili valori iniziali. NSObject dichiara il metodo init come
prototipo per questo scopo. L'oggetto è così pronto per essere usato, è possibile quindi
inviargli messaggi, inoltrarlo ad un altro oggetto e così via. È da notare comunque che
spesso si ingloba l'espressione del messaggio di alloc all'interno del messaggio di init (o di
altri inizializzatori) come nel seguente esempio:
id anObj = [[MyClass alloc] init];
Quando si rilascia un oggetto, attraverso un messaggio release, NSObject decrementa il
suo puntatore retain, se tale valore diventa zero l'oggetto viene deallocato in due fasi. In
una prima fase, il metodo dealloc dell'oggetto è invocato per rilasciare le istanze delle
variabili e liberare la memoria allocata dinamicamente. Successivamente il sistema
operativo distrugge l'oggetto e reclama la memoria che era occupata dall'oggetto stesso. È
importante notare che non si dovrebbe mai invocare direttamente un metodo dealloc di un
oggetto.
Nel caso in cui, ricevuto un oggetto, si richiede il suo mantenimento, il contatore retain
dell'oggetto verrà incrementato a due, saranno quindi necessari due messaggi di release
per rilasciare tale oggetto. In Figura 15 è indicato questo meccanismo.
Figura 14: Contatore retain
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
112
Nel caso descritto in Figura 15, il creatore dell'oggetto non ha necessità di trattenerlo in
quanto lo possiede già, ma se questo è stato creato per inviarlo ad un altro oggetto tramite
un messaggio la situazione cambia. In un programma Objective-C è possibile ricevere
oggetti da altri oggetti ed inviarli per messaggio ad altri oggetti ancora. Questa
caratteristiche richiede che se un primo oggetto invia un oggetto ad un terzo che fa da
ricevente, il primo non può rilasciare il secondo oggetto prematuramente mentre questo è
utilizzato dall'ultimo ricevente.
Nel caso in cui il ricevente dell'oggetto necessita di conservarlo anche quando il
programma ha concluso il suo scopo può farlo, incrementando il contatore retain
dell'oggetto. In tal modo se il creatore dell'oggetto lo rilascia questo non verrà deallocato
ed il ricevente diventa il responsabile dello stesso, potrà così deallocarlo in un momento
successivo. Nella Figura 16 è descritta questa dinamica.
Anziché mantenere un oggetto è possibile copiarlo attraverso l'invio di una copia o con un
messaggio copyWithZone:, molte sottoclassi che incapsulano un dato utilizzano questo
Figura 15: Trattenuta di un oggetto ricevuto
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
113
protocollo. Copiando un oggetto, non solo lo si duplica ma si riporta il contatore retain ad
uno (come indicato in Figura 17).
La copia può essere superficiale o profonda, dipende dalla natura dell'oggetto e dal suo
utilizzo. Una copia profonda duplica l'oggetto e le sue variabili istanziate, mentre una
copia superficiale duplica solo i riferimenti a tali variabili.
In termini di utilizzo, ciò che differenzia una copia dall'originale è che adesso l'oggetto è
gestito soltanto dal nuovo proprietario, che può cambiare tale copia senza preoccuparsi
della sua origine.
Generalmente si utilizza questa tecnica con gli oggetti mutabili come un
NSMutableString, mentre per quelli immutabili, copiarli o trattenerli è praticamente
equivalente.
In una tale gestione degli oggetti si potrebbe verificare un problema nel suo ciclo di vita.
Se infatti un oggetto ne crea un altro e poi lo invia ad un terzo potrebbe non sapere quando
poterlo rilasciare in sicurezza, ci potrebbero essere infatti molteplici riferimenti all'oggetto
Figura 16: Oggetti copiati e ricevuti
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
114
nello stack delle chiamate e queste potrebbero essere sconosciute alla creazione
dell'oggetto. Se l'oggetto creatore lo rilasciasse ed altri oggetti inviassero un messaggio
all'oggetto distrutto il programma potrebbe andare in crash. Per evitare ciò Cocoa
introduce un meccanismo per dilazionare la deallocazione, chiamata “autorilascio”.
L'autorilascio si avvale di autorelease pool (definite dalla classe NSAutoreleasePool). Un
autorelease pool è una collezione di oggetti all'interno di un ambito definito
esplicitamente, contrassegnati per il loro eventuale rilascio, le autorelease pool possono
essere nidificate. Quando si invia un messaggio di autorilascio ad un oggetto si mette il
riferimento all'oggetto nella autorelease pool del suo ambito. Tale oggetto è ancora valido,
così che altri oggetti che rientrano nell'ambito di applicazione definito dall' autorelease
pool possono inviargli messaggi. Quando un programma conclude la sua esecuzione
l'autorelease pool viene rilasciata e con essa tutti gli oggetti al suo interno (guarda figura
18). Non è comunque sempre necessario configurare un autorelease pool in quanto
Application Kit lo crea automaticamente nell'ambito dell'applicazione.
Figura 17: Autorelease Pool
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
115
Poiché in iPhone OS le applicazioni sono eseguite in un ambiente con memoria limitata
l'uso dell'autorilascio è sconsigliato, sopratutto in metodi o blocchi di codice (come i cicli)
in cui si creano molti oggetti, invece è consigliato il rilascio esplicito ovunque sia
possibile.
In sostanza la politica di proprietà degli oggetti può essere sintetizzata nel seguente modo:
1 Se si crea un oggetto allocandolo ed inizializzandolo (ad esempio con la direttiva
[[MyClass alloc] init]) si è proprietari dell'oggetto e responsabili del suo rilascio. Questo
ruolo si verifica anche con l'utilizzo del metodo pubblico new di NSObject.
2 Se si copia un oggetto, si è proprietari della copia e responsabili del suo rilascio.
3 Se si trattiene un oggetto, si è parzialmente proprietari e bisogna rilasciarlo quando non è
più necessario trattenerlo.
Viceversa, se si riceve un oggetto da un altro oggetto, non si è proprietari di questo e non si
dovrebbe rilasciarlo.
È importante notare come sia poco utile non seguire la politica appena descritta, in quanto
il programma potrebbe rivelarsi poco efficiente. Non deallocare oggetti creati, copiati o
trattenuti infatti rende il programma avido di memoria o addirittura andare in crash nel
momento in cui si invia un messaggio ad un oggetto deallocato in maniera non controllata.
Altra considerazione da fare è che questo tipo di problemi prevedono tempi di debug
molto lunghi per essere individuati.
4.3.5 Archiviazione di oggetti
Un'ulteriore possibile operazione da effettuare su di un oggetto durante il suo ciclo di vita
è l'archiviazione. Questo vuol dire che si converte la rete di oggetti di un programma (che
diventa un oggetto grafico) in una forma persistente, generalmente un file. Si salva quindi
l'identità e la rete di collegamenti di ciascun oggetto del grafico. Quando il programma è
recuperato il suo oggetto grafico è ricostruito dal suo archivio. Per essere incluso
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
116
nell'archiviazione e nel recupero un oggetto deve poter codificare e decodificare le sue
variabili usando i metodi della classe NSCoder, a questo scopo NSObject utilizza il
protocollo NSCoding.
4.3.6 Usare Objective
Un programma viene eseguito attraverso lo scambio di messaggi, un oggetto invia
messaggi ad altri oggetti. Attraverso i messaggi, l'oggetto mittente richiede qualcosa
all'oggetto ricevitore (receiver), può richiedere che il ricevitore svolga un'azione,
restituisca un oggetto, un valore o altre possibili richieste.
Objective-C adotta una forma sintattica unica per i messaggi:
NSEnumerator *enm = [sorted_args objectEnumerator];
Il messaggio è sul lato destro dell'espressione, tra le parentesi quadre. La voce più a
sinistra nell'espressione del messaggio è il ricevitore, una variabile o una espressione che
rappresenta l'oggetto al quale è stato inviato il messaggio. In questo caso, il ricevitore è
sorted_args, un'istanza della classe NSArray.
Dopo il ricevitore c'è proprio il messaggio, in questo caso objectEnumerator. Il messaggio
ObjectEnumerator invoca un metodo di un oggetto sorted_args denominato
objectEnumerator, che restituisce un riferimento ad un oggetto detenuto dalla variabile
enm sul lato sinistro dell'espressione. Questa variabile è di tipo statico come un'istanza
della classe NSEnumerator. È possibile uno schema di questa dichiarazione:
NSClassName variable[receiver message];
Tuttavia, questo schema è semplicistico e non molto accurato, un messaggio è costituito da
un nome di selettore e dai parametri del messaggio. Il runtime Objective-C utilizza un
nome di selettore, come objectEnumerator dell'esempio precedente, per cercare il selector
nella tabella di dispach, al fine di trovare il metodo da richiamare. Un selettore (SEL) è un
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
117
identificatore univoco che rappresenta un metodo. Poichè è così strettamente connesso, il
nome utilizzato per cercare il selettore è spesso chiamato come il selettore stesso. La
dichiarazione di cui sopra può quindi essere correttamente riscritta:
NSClassName variable[receiver selector];
I messaggi spesso sono parametri, o argomenti. Un messaggio con un unico argomento
appone due punti dopo il nome del selettore e pone l'argomento alla destra dei due punti.
Una parola chiave si conclude con due punti, l'argomento viene posto dopo di essi. Così
potremmo costruire l'espressione di un messaggio con un unico argomento:
NSClassName variable[receiver keyword: argument];
Se un messaggio ha più argomenti, il selettore ha più keyword. Un nome di selettore
include tutte le keyword, include i due punti ma non include nient'altro, come il tipo di
ritorno o il tipo di parametro.
L'espressione di un messaggio con più argomenti potrebbe essere la seguente:
NSClassName variable[receiver keyword2: arg1 keyword2: arg2];
come i parametri di funzione, il tipo di un argomento deve corrispondere al tipo
specificato nel metodo dichiarato. Prendiamo come esempio la sintassi del seguente
messaggio del programma SimpleCocoaTool:
NSCountedSet *cset = [[NSCountedSet alloc]
initWithArray:args];
Nell'espressione si ha che args, che è spesso un'istanza della classe NSArray, è l'argomento
del massaggio chiamato initWithArray:.
L'esempio citato è interessante in quanto illustra la nidificazione. Con Objective-C, è
possibile nidificare un messaggio all'interno di un altro messaggio; l'oggetto restituito
dall'espressione di un messaggio è usato come il ricevitore dall'espressione del messaggio
che racchiude. Così, per interpretare espressioni nidificate di messaggi, si inizia con
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
118
l'espressione interna e si lavora verso l'esterno. L'interpretazione della dichiarazione di cui
sopra potrebbe essere:
1. Il messaggio alloc è inviato alla classe NSCountedSet, che crea un'istanza non
inizializzata della classe.
2. Il messaggio initWithArray: è inviato ad un'istanza non inizializzata, che si inizializza
con l'array args e restituisce un riferimento a sé stesso.
Si consideri ora quest'espressione dalla routine main di SimpleCocoaTool:
NSArray *sorted_args = [[cset allObjects]
sortedArrayUsingSelector:@selector(compare:)];
In quest'espressione è degno di nota l'argomento del messaggio
sortedArrayUsingSelector:. Questo argomento richiede l'uso della direttiva di compilatore
@selector per creare un selettore da essere usato come argomento.
Riguardiamo ora la terminologia di messaggi e metodi. Un metodo è essenzialmente una
funzione definita ed implementata da una classe di cui il destinatario del messaggio è
membro. Un messaggio è un nome di selettore, costituito anche di una o più parole chiave,
e dai suoi argomenti. Un messaggio viene inviato ad un ricevitore e ciò comporta
l'invocazione o l'esecuzione del metodo.
La sintassi del messaggio comprende sia il ricevitore che il messaggio, in figura 19 è
spiegata questa relazione.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
119
Figura 18: sintassi di un messaggio
Objective-C utilizza un numero definito di tipi e letterali che non si trova in ANSI C e che,
in alcuni casi, sostituiscono le loro controparti ANSI C.
La Tabella 3 descrive questi elementi ed i letterali ammessi per ogni tipo.
Tipi Descrizioni e letterali
Id Un tipo dinamico, il suo letterale negativo è Nil.
Class Classe di tipo dinamico, il suo letterale negativo è Nil.
SEL Il tipo dato di un selettore (typedef). Il suo letterale negativo è NULL.
BOOL Un tipo Booleano, il suo valore letterale è YES o NO.
Tabella 2: Elementi e descrizioni
Nel flusso di controllo delle dichiarazioni di un programma, è possibile verificare la
presenza (o l'assenza) di un determinato valore negativo per scegliere come procedere. Per
esempio, nel programma SimpleCocoaTool, si testa implicitamente un oggetto variabile
word per determinare la presenza di un oggetto restituito, quindi dell'assenza di un valore
nullo:
while (word = [enm nextObject]) {
printf("%s\n", [word UTF8String]);
}
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
120
In Objective-C è possibile inviare un messaggio nil senza avere nessun effetto negativo, il
valore di ritorno garantisce la prosecuzione delle operazioni come se fosse stato restituito
un oggetto.
L'ultima caratteristica da notare in Objective-C è poco evidente, ma si guardi il seguente
stralcio di codice:
NSEnumerator *enm = [sorted_args objectEnumerator];
E lo si paragoni al seguente:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Apparentemente sembrano identici, entrambi restituiscono un riferimento ad un oggetto,
tuttavia c'è una grossa differenza semantica (per la gestione della memoria), che ha a che
fare con la proprietà degli oggetti restituiti e con la responsabilità del proprio rilascio.
Nella prima dichiarazione non viene restituito l'oggetto, nel secondo stralcio di codice il
programma crea l'oggetto e lo possiede. L'ultima cosa che fa il programma è inviare un
messaggio di rilascio all'oggetto creato, così da liberarlo. L'altro e unico oggetto creato
esplicitamente (ad esempio NSCountedSet) è esplicitamente rilasciato alla fine del
programma SimpleCocoaTool.
4.4 Objective-C: aspetti avanzati
Objective-C è dotato di due caratteristiche per il linguaggio base che sono potenti
strumenti di sviluppo software: Categorie e Protocolli. Alcune estensioni introducono
tecniche differenti per la dichiarazione di metodi e della loro associazione alle classi. Altre
tecniche offrono strade semplici per dichiarare ed accedere alle proprietà degli oggetti,
enumerare velocemente collection, gestire le eccezioni e migliorare altre operazioni.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
121
4.4.1 Categorie
Le categorie permettono di attribuire metodi alle classi senza dover costruire sottoclassi, i
metodi nelle categorie diventano parti del tipo di classe, entro l'ambito del programma, e
sono ereditate da tutte le sottoclassi. Non ci sono differenze al tempo di esecuzione tra i
metodi originali e quelli aggiunti, è possibile inoltre inviare un messaggio ad ogni istanza
della classe (o una sua sottoclasse) per invocare un metodo definito in una categoria.
Le categorie sono molto più che un modo conveniente di aggiungere azioni alle classi, è
possibile usare le categorie per raggruppare metodi, e dividerli in categorie differenti. Le
categorie sono altresì uno strumento utile per organizzare classi molto grandi e si possono
anche mettere diverse categorie in diversi file sorgenti se, per esempio, ci sono diversi
sviluppatori che lavorano sulla stessa classe.
Dichiarare ed implementare una categoria è molto simile che farlo per una sottoclasse,
sintatticamente l'unica differenza è il nome della categoria che segue la direttiva
@interface o @implementation ed è scritto tra parentesi. Se per esempio vogliamo
aggiungere un metodo alla classe NSArray che stampa la descrizione di collection in più
di una struttura, nell'header file della categoria bisognerà scrivere un codice simile al
seguente:
#import <Foundation/NSArray.h> // if Foundation not already
imported
@interface NSArray (PrettyPrintElements)
- (NSString *)prettyPrintDescription;
@end
Nel file di implementazione invece il codice sarà:
#import “PrettyPrintCategory.h”
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
122
@implementation NSArray (PrettyPrintElements)
- (NSString *)prettyPrintDescription {
// implementation code here...
}
@end
Bisogna comunque sottolineare le limitazioni che le categorie hanno, non è possibile
infatti usare una categoria per aggiungere istanze di nuove variabili alle classi. Benchè una
categoria di metodi può sovrascrivere un metodo esistente, non è raccomandabile farlo,
sopratutto se si vogliono incrementare le caratteristiche. Una ragione per questa cautela è
che i metodi delle categorie sono parti delle interfacce delle classi, e non c'è modo di
inviare un messaggio per realizzare le caratteristiche già definite dalla classe. Se risultasse
necessario cambiare un metodo esistente, è consigliabile creare una sottoclasse.
È possibile definire le categorie per aggiungere metodi alla classe root, NSObject. Tali
metodi sono disponibili per tutte le istanze e le classi di oggetti che sono collegati al
codice. Protocolli informali, la base per il meccanismo di delega di Cocoa, sono dichiarati
come categorie di NSObject. Questo approccio però comporta dei rischi, in quanto queste
caratteristiche aggiunte ad ogni oggetto attraverso una categoria di NSObject, potrebbero
comportare conseguenze che non sempre possono essere anticipate, portando al blocco del
programma, corruzione dei dati, o conseguenze peggiori.
4.4.2 Protocolli
Le interfacce di categorie e classi dichiarano metodi associati ad una particolare classe. I
protocolli, formali ed informali invece dichiarano metodi indipendenti da una specifica
classe ma che qualunque classe può implementare ed utilizzare.
I protocolli quindi sono utili in almeno tre situazioni:
1 per dichiarare metodi che si prevede di implementare al di fuori di una specifica classe
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
123
2 per dichiarare l'interfaccia ad un oggetto che nasconde la propria classe
3 per collegare classi che non sono gerarchicamente connesse
Un protocollo in sostanza è una lista di dichiarazioni di metodi slegati da una specifica
classe. Ad esempio se si costruisce un protocollo che nei suoi metodi gestisce le azioni del
mouse, ogni classe che vuole sfruttare le caratteristiche del mouse adotterà tale protocollo
ed implementerà i suoi metodi. I protocolli quindi rendono libera una dichiarazione di
metodo dalle classi e dalle loro gerarchie. L'unico vincolo che ha una classe per poter
utilizzare il metodo dichiarato è la conformità al protocollo. I protocolli sono inoltre
estremamente utili in progetti divisi in più blocchi o quando incorporano oggetti di altri
progetti.
Il protocollo è anche il modo per una classe di dichiarare un'interfaccia quando nasconde
la propria identità. Le interfacce possono inoltre esporre tutti o solo un range di servizi
disponibili della classe che la offre. Qualunque altra classe in questo modo può
implementare i metodi del protocollo e così accedere ai servizi pubblicati. Con un
protocollo quindi è possibile mettere in comunicazione classi non relazionate in alcun
modo.
Un protocollo può inoltre dichiarare metodi per un oggetto di una classe “sconosciuta”.
Tale oggetto sconosciuto potrebbe infatti rappresentare un set di funzioni. Un esempio di
protocollo di oggetti sconosciuti potrebbe verificarsi nell'eventualità di dover inviare un
messaggio ad un oggetto remoto, quindi di un'applicazione esterna. Ovviamente in tale
applicazione deve essere implementato un protocollo atto a ricevere tale messaggio.
Esistono due tipi di protocolli: formale ed informale. I protocolli informali sono stati
brevemente introdotti nel paragrafo 4.4.1 “Categorie”. Sono le categorie in NSObject, di
conseguenza ogni oggetto con NSObject come sua superclasse implicitamente adotta
l'interfaccia pubblicata nella categoria.
Per utilizzare un protocollo informale, una classe non deve implementare tutti i suoi
metodi, ma soltanto quello a cui è interessato, per far funzionare tale protocollo inoltre la
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
124
classe che lo dichiara deve ottenere una risposta positiva a respondsToSelector (messaggio
ricevuto dall'oggetto destinazione) e lo deve ricevere prima di inviare il messaggio con
l'oggetto del protocollo. (Se l'oggetto di destinazione non implementa il metodo ci sarà
una eccezione di runtime.)
I protocolli formali sono generalmente quelli che in Cocoa vengono definiti come
“Protocolli”, consentono ad una classe di dichiarare formalmente una lista di metodi come
interfacce di servizi offerti. Il linguaggio Objective-C ed il sistema di runtime supportano i
protocolli formali; il compilatore può verificare i tipi basati sui protocolli, e gli oggetti
possono al tempo di esecuzione verificare la conformità al protocollo. I protocolli formali
hanno una loro terminologia ed una sintassi, la terminologia è differente per provider
(fornitore) e client:
1 Un provider, generalmente una classe, dichiara il protocollo formale.
2 Un client classe, adotta un protocollo formale e così facendo si impegna ad attuare tutti i
metodi del protocollo.
3 Una classe si dice conforme ad un protocollo formale se adotta il protocollo o lo eredita
da una classe che lo adotta, i protocolli sono quindi ereditati dalle sottoclassi.
Sia la dichiarazione che l'adozione di un protocollo hanno una loro sintassi in Objective-C,
per dichiarare un protocollo è necessario utilizzare la direttiva del compilatore @protocol.
L'esempio seguente (Riquadro 11) mostra la dichiarazione del protocollo NSCoding,
nell'header file NSObject.h del framework Foundation.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
125
@protocol NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder;
@end
Riquadro 11: dichiarazione di Protocollo
Objective-C 2.0 aggiunge caratteristiche ai protocolli formali che permettono la possibilità
di dichiarare metodi opzionali esattamente come quelli richiesti. In Objective-C 1.0
invece, l'adozione di un protocollo, implicava l'implementazione di tutti i metodi del
protocollo. Nei protocolli di Objective-C 2.0 i metodi sono implicitamente considerati
required e possono essere esplicitamente indicati come tali attraverso l'uso della direttiva
@required, allo stesso tempo è possibile marcare blocchi dei metodi del protocollo come
opzionali, attraverso l'uso della direttiva @optional. Tutti i metodi dichiarati dopo questa
direttiva, fino a che non si fa riferimento ancora a @required, sono implementati come
opzionali. Consideriamo la seguente dichiarazione:
@protocol MyProtocol
// implementation this method is required implicitly
- (void)requiredMethod;
@optional
// implementation of these methods is optional
- (void)anOptionalMethod;
- (void)anotherOptionalMethod;
@required
// implementation of this method is required
- (void)anotherRequiredMethod;
@end
La classe che dichiara il protocollo generalmente non implementa i suoi metodi.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
126
Comunque dovrebbe invocare tali metodi nelle istanze delle classi conformi al protocollo.
Prima di invocare metodi opzionali, deve verificare che sono implementati usando
respondsToSelector.
Una classe adotta un protocollo specificando il nome, tra parentesi angolari (<...>), alla
fine della direttiva @interface, appena dopo la superclasse. Una classe può adottare
protocolli multipli delimitati con le virgole. Di seguito è indicato come la classe NSData
Foundation adotta tre protocolli.
@interface NSData : NSObject <NSCopying, NSMutableCopying,
NSCoding>
Con l'adozione di questi protocolli, NSData si impegna ad implementare tutti i metodi
required dichiarati nei protocolli, può anche scegliere di applicare metodi contrassegnati
con la direttiva @optional. Anche le Categorie possono adottare protocolli e la loro
adozione diventa parte della definizione della classe associata.
I tipi Objective-C sono raggruppati sulla base dei protocolli ai quali sono conformi così
come per le classi dalle quali derivano. È possibile verificare se una classe è conforme ad
un particolare protocollo con l'invio di un messaggio conformsToProtocol:
if ([anObject conformsToProtocol:@protocol(NSCoding)]) {
// do something appropriate
}
In una dichiarazione di un tipo (metodo, istanza di variabile o funzione) è possibile
specificare la conformità ad un protocollo come parte del tipo, si ottiene quindi un altro
livello di tipo di controllo da parte del compilatore, più astratto perchè non è legato ad una
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
127
particolare implementazione.
Si utilizza la stessa sintassi come convenzione per l'adozione di protocolli: si inserisce il
nome del protocollo tra parentesi angolari per specificare la conformità del protocollo al
tipo. Spesso si utilizza questa sintassi per gli oggetti dinamici:
- (void)draggingEnded:(id <NSDraggingInfo>)sender;
Nel codice l'oggetto associato all'argomento può essere di ogni tipo di classe, ma deve
essere conforme al protocollo NSDraggingInfo.
Cocoa fornisce diversi esempi di altri protocolli diversi da quelli indicati fin'ora, un
protocollo interessante è NSObject, non sorprende che lo adotta la classe NSObject.
Attraverso questo protocollo l'altra classe di root, NSProxy, può interagire con le parti del
runtime Objective-C essenziali per il conteggio, introspection ed altri aspetti base dei
comportamenti degli oggetti.
4.4.3 Dichiarazione delle proprietà
Nel dichiarare la struttura di un oggetto, bisogna considerare alcune proprietà. Tali
proprietà sono attributi degli oggetti stessi, come titolo e colore, e relazioni con altri
oggetti. Nel codice tradizionale Objective-C è possibile definire proprietà dichiarando
istanze di variabili, attraverso l'incapsulamento e l'implementazione di metodi accessori
getter e setter per i valori di queste variabili. Questa procedura è tediosa e rischiosa,
soprattutto se riguarda la gestione della memoria.
Objective-C 2.0, introdotto in Mac OS X 10.5, offre una sintassi per dichiarare le proprietà
e specificare come accedervi. Dichiarare una proprietà quindi diventa come sintetizzare
metodi come il getter ed il setter. Attraverso le proprietà quindi non è più necessario
implementare i metodi accessori. Ci sono tre aspetti della sintassi delle proprietà:
dichiarazione, implementazione ed accesso.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
128
E' possibile dichiarare proprietà ovunque sia possibile dichiarare metodi nelle sezioni
dichiarative di classi, categorie e protocolli. La sintassi per dichiarare una proprietà è:
@property(attributes...)type propertyName
Dove attibutes... sono uno o più attributi opzionali (se multipli sono separati da virgole),
che influenzano come il compilatore registra le variabili istanziate e sintetizza i metodi
accessori.
L'elemento type specifica un tipo oggetto, un tipo dichiarato o scalare come NSString,
come un id, NSRange o float. La proprietà deve essere sostenuta da una variabile
istanziata dello stesso tipo e nome.
I possibili attributi nelle dichiarazioni di una proprietà sono:
Attributi Effetti
getter=getterName
setter=setterName
Specifica il nome dei metodi accessori getter e setter.
Bisogna specificare questi attributi quando si implementa un metodo
accessorio personalizzato e si vuole modificare il suo nome.
Readonly Indica che può essere solo letta, non scritta. Il compilatore non
sintetizza un accessorio setter.
Readwrite Indica che la proprietà può essere letta e scritta, questo attributo è di
default, se non è specificato readonly.
Assign
Specifica che una semplice assegnazione potrebbe essere usata
nell'implementazione del setter. Questo attributo è di default. Se le
proprietà sono dichiarate in un programma non-garbage-collected, è
necessario conservare o copiare le proprietà degli oggetti.
Retain Specifica che retain deve essere inviato alla proprietà (che deve
essere di un tipo oggetto) al momento dell'assegnazione, inoltre retain
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
129
è un no-op in un ambiente garbage-collected.
Copy
Specifica che copy deve essere inviato alla proprietà (che deve essere
un tipo oggetto) al momento dell'assegnazione. La classe dell'oggetto
deve implementare il protocollo NSCopying.
Nonatomic
Specifica che i metodi accessori sono sintetizzati come nonatomic. Di
default quindi tutti i metodi accessori sono atomici, un metodo getter
è garantito per restituire un valore valido, anche quando ci sono in
esecuzione altri thread contemporaneamente.
Se non si specificano attributi e si specifica @synthesize per l'implementazione, il
compilatore sintetizza metodi setter e getter per le proprietà che usano assegnazioni
semplici e che hanno propertyName per il getter e setPropertyName per il setter.
Nel blocco @implementation di una definizione di classe, è possibile usare direttive
@dynamic e @synthesize per controllare quando il compilatore sintetizza metodi accessori
per particolari proprietà.
Entrambe le direttive hanno la stesa sintassi generale :
@dynamic propertyName [, propertyName2...];
@synthesize propertyName [, propertyName2...];
La direttiva @dynamic dice al compilatore che è stato implementato un metodo accessorio
per una proprietà, diretto o dinamico. La direttiva @synthesize d'altro canto dice al
compilatore di sintetizzare i metodi setter e getter se non compaiono nel blocco
@implementation. La sintassi per @syntetize spesso include un'estensione che permette di
nominare una proprietà differentemente dalla sua variabile istanziata. Consideriamo la
seguente dichiarazione:
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
130
@synthesize title, directReports, role = jobDescrip;
Questa dichiarazione dice al compilatore di sintetizzare metodi accessori per le proprietà
title, diretcReports e role, e di usare jobDescrip per indicare la proprietà role.
In conclusione la caratteristiche delle proprietà in Objective-C supportano una sintassi
semplificata per i metodi accessori (getting e setting) attraverso l'uso di notazioni puntate e
di semplici assegnazioni. Bisogna notare infine che l'uso delle notazioni puntate funziona
solo con attributi e relazioni semplici uno-ad-uno, non con relazioni multiple.
Pochi esempi bastano a dimostrare quanto sia facile ottenere i valori delle proprietà e
l'utilizzo di questa sintassi:
NSString *title = employee.title; // assigns employee title to
local variable
employee.ID = "A542309"; // assigns literal string to employee ID
// gets last name of this employee's manager
NSString *lname = employee.manager.lastName;
4.4.4 Fast Enumeration
“Fast enumeration” (enumerazione veloce) è una caratteristica introdotta in Objective-C
2.0 che restituisce un modo sintetico per numerare efficientemente le collection
(collezioni). Questo metodo è estremamente più veloce del classico uso di oggetti
NSEnumerator per scorrere attraverso array, set e dizionari. Inoltre garantisce sicurezza,
come mutation guard, che previene modifiche alle collection durante una enumerazione,
sollevando un'eccezione al tentativo di mutazione.
La sintassi per la numerazione veloce è simile a quella usata nei linguaggi di scripting
come Perl e Ruby, ci sono due versioni supportate:
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
131
for ( type newVariable in expression ) { statements }
e
type existingVariable;
for( existingVariable in expression ) { statements }
L'espressione deve valutare un oggetto la cui classe è conforme al protocollo
NSFastEnumeration. L'implementazione di fast enumeration è condivisa tra il runtime
Objective-C ed il framework Foundation. Foundation dichiara il protocollo
NSFastEnumeration, le classi collection di Foundation (NSArray, NSDictionary, e NSSet)
e la classe NSEnumerator adottano tale protocollo. Altre classi che detengono collezioni di
altri oggetti, comprese le classi personalizzate, possono adottare NSFastEnumeration per
sfruttare questa caratteristica.
Il seguente stralcio di codice illustra come sia possibile usare l'enumerazione veloce con
oggetti NSArray e NSSet:
NSArray *array = [NSArray arrayWithObjects:
@"One", @"Two", @"Three", @"Four", nil];
for (NSString *element in array) {
NSLog(@"element: %@", element);
}
NSSet *set = [NSSet setWithObjects:
@"Alpha", @"Beta", @"Gamma", @"Delta", nil];
NSString *setElement;
for (setElement in set) {
NSLog(@"element: %@", setElement);
}
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
132
4.4.5 Selector
Come introdotto nel paragrafo 4.3.1 “I vantaggi di Objective-C”, i selector sono elementi
della tabella di dispach contenuta nella struttura di ogni classe.
Quando un oggetto riceve un messaggio, attraverso il puntatore isa accede alla propria
classe per cercare nella tabella di dispach il selector del metodo da attuare. Nel caso in cui
tale selector non venga trovato la ricerca procede attraverso il puntatore alla superclasse
lungo tutta la catena gerarchica. Tale meccanismo viene realizzato quando il metodo da
eseguire viene ricavato al tempo di esecuzione.
Bisogna notare altre due caratteristiche che velocizzano lo scambio di messaggi al tempo
di esecuzione. Innanzitutto il sistema runtime memorizza i selector e gli indirizzi dei
metodi usati. Vengono memorizzati in modo separato classe per classe, permettendo di
memorizzare i selector sia dei metodi implementati nella classe sia quelli ereditati. In tal
modo il sistema runtime, quando un oggetto riceve un messaggio, cerca il selector prima
tra quelli già utilizzati, se non lo trova procede con la ricerca all'interno della tabella di
dispach della classe.
Per migliorare l'efficienza inoltre, all'interno del codice compilato, non si associa ai
selector il nome completo del metodo a cui fanno riferimento. Il compilatore scrive nella
tabella il metodo e gli attribuisce un nome di selector univoco. In tal modo non esistono
selector uguali ed allo stesso tempo i metodi con lo stesso nome hanno lo stesso selector.
Successivamente alla compilazione i selector sono identificati da un tipo di dato speciale
SEL che si distingue dagli altri tipi dati. Un selector valido non assume mai valore nullo.
4.4.6 Creazione di oggetti
La creazione di un oggetto Cocoa avviene in due fasi: allocazione ed inizializzazione.
Senza di esse generalmente un oggetto non è usabile, di conseguenza nella maggior parte
dei casi l'inizializzazione segue sempre l'allocazione nonostante giochino ruoli differenti
nella formazione di un oggetto.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
133
4.4.6.1 Allocazione
Quando si alloca un oggetto Cocoa si destina memoria all'oggetto da una regione di
memoria virtuale associata all'applicazione. Per valutare la quantità di memoria da allocare
si prendono in considerazione le variabili di istanza dell'oggetto, incluso i tipi ed il loro
ordine, come specificato dalla classe dell'oggetto.
Per allocare un oggetto bisogna inviare un messaggio alloc o allocWithZone: alla classe
dell'oggetto, sarà restituita un'istanza grezza (non inizializzata) di quella classe. La
variante alloc utilizza la zona di memoria destinata all'applicazione. Un messaggio di
allocazione compie anche altre operazioni:
• Configura il contatore retain dell'oggetto ad uno.
• Inizializza il puntatore isa dell'oggetto indicandogli la classe dell'oggetto stesso, un
oggetto deve essere compilato dalla definizione della classe.
• Inizializza tutte le variabili a zero (o valori equivalenti come nil, NULL e 0.0).
L'oggetto isa è ereditato da NSObject, così è in comune a tutti gli oggetti Cocoa. Dopo
l'allocazione viene configurato l'isa alla classe dell'oggetto che così viene integrato nella
vista runtime della gerarchia dell'ereditarietà delle classi e nella rete di oggetti (classi od
istanze) che costituiscono il programma. Conseguentemente un oggetto può trovare tutte le
informazioni di cui necessita al tempo di esecuzione, come un altro posto nella gerarchia,
il protocollo a cui sono conformi altri oggetti o ancora le implementazioni dei metodi che
possono servire per rispondere ad un messaggio.
In sostanza per “allocazione” non si intende solo l'allocazione della memoria per un
oggetto, ma l'inizializzazione di due piccoli ma molto importanti attributi di tutti gli
oggetti: la variabile puntatore isa ed il suo contatore retain. Inoltre si inizializzano a zero
tutte le variabili ad esso associate, ma l'oggetto non è ancora usabile, metodi di
inizializzazione come init restituiscono l'oggetto usabile, ricco di tutte le sue
caratteristiche.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
134
4.4.6.2 Inizializzazione
L'inizializzazione imposta le variabili di istanza di un oggetto a valori iniziali coerenti per
le variabili stesse. Inoltre si possono allocare e disporre altre risorse globali di cui l'oggetto
ha bisogno, caricandole da una risorsa esterna come un file. Ogni oggetto che dichiara una
variabile dovrebbe implementare un metodo di inizializzazione, a meno che non sia
sufficiente l'inizializzazione di default che imposta tutte le variabili a zero. Se inoltre un
oggetto non implementa un metodo di inizializzazione, Cocoa invoca il metodo
dell'oggetto a lui più vicino dal punto di vista gerarchico.
4.4.6.2.1 Forme di inizializzazione
NSObject dichiara il prototipo init per le inizializzazioni, è un tipo di metodo che
restituisce un oggetto di tipo id. Alcune sottoclassi infatti utilizzano init quando non
richiedono ulteriori dati per inizializzare i propri oggetti, spesso invece l'inizializzazione
dipende da dati esterni per impostare un oggetto al suo stato iniziale. Per esempio se si è
costruiti una classe Account, per inizializzare un oggetto account è necessario fornire al
metodo di inizializzazione il numero dell'account. Questo vuol dire che i metodi di
inizializzazione possono avere uno o più argomenti di ingresso, l'unico vincolo posto è che
il nome inizi con “init”. In questo paragrafo si farà riferimento ad un generico
inizializzatore indicandolo con “init...”.
Bisogna inoltre notare che invece di implementare un metodo di inizializzazione con
argomenti in ingresso è anche possibile che una sottoclasse implementi solo un semplice
metodo init e successivamente usi un metodo accessorio set per impostare lo stato iniziale
dell'oggetto.
Cocoa offre numerosi esempi di inizializzatori con argomenti, di seguito ne sono elencato
alcuni:
• (id)initWithArray:(NSArray *)array; (da NSSet)
• (id)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
135
*)anotherDate; (da NSDate)
• (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)aStyle
backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag; (da NSWindow)
• (id)initWithFrame:(NSRect)frameRect; (da NSControl e NSView)
Questi inizializzatori sono metodi il cui nome inizia con “init” e restituiscono un oggetto
di un tipo id dinamico, inoltre seguono le convenzioni Cocoa per i metodi multi-
argomento, spesso infatti usano WithType: o FromSource: prima del primo (e più
importante) argomento.
4.4.6.2.2 Possibili problemi con metodi init…
Anche se i metodi init... devono restituire un oggetto, non necessariamente però tale
oggetto è l'ultimo allocato (il ricevitore del messaggi di init). In sostanza è possibile che
l'oggetto restituito da un init... potrebbe non essere quello che si voleva inizializzare.
Due situazioni posso portare portare alla restituzione di un oggetto diverso da quello
allocato. La prima implica due situazioni, se deve esistere una istanza singleton oppure se
la definizione di un attributo di un oggetto deve essere univoca. Molte classi Cocoa, per
esempio NSWorkspace, abilitano un'unica istanza in un programma, una classe in tal caso
deve garantire che viene creata una sola istanza, restituendola se perviene un'ulteriore
richiesta di istanza.
Una situazione simile si verifica quando un oggetto richiede un attributo particolare per
essere unico. Ricordando l'esempio della classe Account, un oggetto di tale classe deve
avere un identificatore unico. Nel caso in cui l'inizializzatore, chiamato ad esempio
initWithAccountID: riceve in ingresso un identificatore che già è stato associato ad un altro
oggetto, possono verificarsi due ipotesi:
• Si rilascia l'oggetto appena allocato.
• Si restituisce l'oggetto account precedentemente inizializzato con quell'identificatore.
In questo modo l'inizializzatore si assicura l'unicità dell'identificatore fornendo al tempo
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
136
stesso la risposta a ciò che gli era stato chiesto, un oggetto con un identificatore unico.
A volte un metodo init... non può realizzare una richiesta di inizializzazione. Ad esempio,
un metodo initFromFile: necessita per inizializzare un oggetto dal contenuto di un file di
un path come parametro di ingresso. Potrebbe però accadere che il path sia errato e
l'oggetto non può essere inizializzato.
Quando un metodo init... non può inizializzare un oggetto può proseguire in due modi
diversi:
• Rilascia l'oggetto appena allocato.
• Restituisce un valore nullo.
Restituire un valore nullo indica che l'oggetto non può essere creato, questo suggerisce che
generalmente bisogna controllare il valore restituito dall'inizializzatore prima di procedere
con il programma:
id anObject = [[MyClass alloc] init];
if (anObject) {
[anObject doSomething];
// more messages...
} else {
// handle error
}
Poiché un metodo init... potrebbe restituire un valore nullo o un oggetto diverso da quello
allocato, non è sicuro usare l'istanza restituita da alloc oppure allocWithZone: al posto di
quello restituito dall'inizializzatore. Si consideri il codice seguente:
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
137
id myObject = [MyClass alloc];
[myObject init];
[myObject doSomething];
Riquadro 12: Oggetto restituito da init
Il metodo init nel Riquadro 12 potrebbe restituire un valore nullo oppure sostituire
l'oggetto con uno differente. Poiché è possibile inviare un messaggio nullo senza sollevare
un'eccezione non accadrebbe nulla nel primo caso, ma per evitare sostituzioni errate si
deve sempre fare affidamento sull'oggetto inizializzato invece dell'oggetto grezzo appena
allocato. È raccomandabile quindi costruire il messaggio di allocazione ed inizializzazione
e testare l'oggetto restituito prima di procedere con le successive operazioni.
id myObject = [[MyClass alloc] init];
if ( myObject ) {
[myObject doSomething];
} else {
// error recovery...
}
Appena inizializzato un oggetto non lo si dovrebbe inizializzare una seconda volta, spesso
questa operazione genera un'eccezione da parte del framework della classe dell'oggetto.
Ad esempio, la seconda inizializzazione nell'esempio che segue potrebbe generare
un'eccezione.
NSString *aStr = [[NSString alloc] initWithString:@"Foo"];
aStr = [aStr initWithString:@"Bar"];
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
138
4.4.6.2.3 Costruire un inizializzatore
Ci sono diversi passaggi critici da seguire al momento di implementare un metodo init...
che serve come unico inizializzatore della classe:
• Deve sempre invocare per prima la superclasse di inizializzatori.
• Verificare l'oggetto restituito dalla superclasse. Se è un valore nullo allora
l'inizializzazione non deve procedere.
• Quando si inizializzano variabili riferite ad oggetti, bisogna trattenere una copia
dell'oggetto.
• Dopo aver impostato una variabile ai valori iniziali deve restituirla, a meno che:
o Non sia stato necessario restituire un oggetto che la sostituisce, in questo caso
bisogna rilasciare l'oggetto appena allocato.
o Un problema di inizializzazione impedisce il proseguimento, in questo caso deve
restituire un valore nullo.
Un metodo init... che applica i punti 2 e 3 appena illustrati è indicato nel Riquadro 13:
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
139
- (id)initWithAccountID:(NSString *)identifier {
if ( self = [super init] ) {
Account *ac = [accountDictionary objectForKey:identifier];
if (ac) { // object with that ID already exists
[self release];
return [ac retain];
}
if (identifier) {
accountID = [identifier copy]; // accountID is
instance variable
[accountDictionary setObject:self forKey:identifier];
return self;
} else {
[self release];
return nil;
}
} else
return nil;
}
Riquadro 13: Inizializzatore
Non è necessario inizializzare tutte le variabili di un oggetto, ma giusto quelle necessarie
al suo funzionamento, in genere è sufficiente l'inizializzazione predefinita che imposta a
zero le variabili da inizializzare. Bisogna inoltre assicurarsi che vengano trattenute copie
delle variabili di istanza come richiesto da memory management.
L'obbligo di richiamare per primo l'inizializzatore della superclasse è importante, si deve
tener presente infatti che un oggetto ingloba non solo le istanze della sua classe, ma anche
quelle di tutte le classi della sua gerarchia. Ciò vuol dire che, invocando per primo
l'inizializzatore della superclasse si garantisce che vengano inizializzati tutti gli oggetti
ereditati, rispettando la catena gerarchica. La classe invoca l'inizializzazione della classe
da cui discende e così facendo si risale la catena come indicato nella Figura 20. Il rispetto
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
140
dell'ordine è critico in quando il ritardo nell'inizializzazione dipende dal numero di
variabili che le superclassi hanno e dai valori a cui devono essere impostate.
La gerarchia degli inizializzatori è importante quando si crea una sottoclasse. A volte il
metodo init... della superclasse è sufficiente per inizializzare le istanze della sottoclasse.
Potrebbe però non essere così, quindi lo si dovrebbe sovrascrivere. Se non lo si
sovrascrive invece verrà invocata l'implementazione della superclasse e dato che la
superlcasse non conosce nulla della sottoclasse, le istanze potrebbero non essere
inizializzati correttamente.
Figura 19: Gerarchia di inizializzazione
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
141
4.4.6.2.4 Inizializzatori multipli ed inizializzatori designati
Una classe può definire più di un inizializzatore, questi possono offrire la stessa
inizializzazione accettando però diversi tipi di input. La classe NSSet, per esempio,
fornisce diversi inizializzatori che accettano gli stessi dati ma in forme diverse, uno accetta
un oggetto NSArray, un altro una lista di contatori di elementi ed un altro una lista di
elementi che terminano con un valore nullo.
- (id)initWithArray:(NSArray *)array;
- (id)initWithObjects:(id *)objects count:(unsigned)count;
- (id)initWithObjects:(id)firstObj, ...;
Alcune sottoclassi trovano conveniente che gli inizializzatori forniscano valori predefiniti
ad un inizializzatore che prende la serie completa di tutti i parametri per l'inizializzazione.
Questo inizializzatore è generalmente l'inizializzatore detto designato ed è il più
importante della classe. Ad esempio, si consideri una classe Task che dichiara un
inizializzatore designato con la seguente dichiarazione:
- (id)initWithTitle:(NSString *)aTitle date:(NSDate *)aDate;
La classe Task potrebbe includere inizializzatori secondari che semplicemente invocano il
designato passandogli i valori di default per quei parametri che gli inizializzatori secondari
non hanno esplicitamente richiesto.
- (id)initWithTitle:(NSString *)aTitle {
return [self initWithTitle:aTitle date:[NSDate date]];
}
- (id)init {
return [self initWithTitle:@”Task”];
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
142
}
Il designato gioca un ruolo importante per una classe, assicura infatti che le variabili
ereditate sono inizializzate invocando il designato della superclasse. Tipicamente è il
metodo init... che ha più argomenti in ingresso e che svolge la maggior parte del lavoro.
Inoltre è quello che gli inizializzatori secondari della classe invocano tramite messaggi.
Quando si definisce una sottoclasse si deve conoscere il designato della superclasse ed
invocarlo nel designato della sottoclasse attraverso un messaggio. Bisogna inoltre
assicurarsi che tutti gli inizializzatori ereditati sono collegati allo stesso modo. Quando si
definisce un inizializzatore di una classe bisogna quindi tener presente che tutti i designati
sono concatenati tra loro tramite messaggi alle superclassi e che tutti gli inizializzatori
sono collegati ai designati delle proprie classi attraverso messaggi.
Un esempio che può chiarire tale dinamica è il seguente:
Si considerino tre classi: A, B e C, ereditate una dall'altra. Ogni sottoclasse aggiunge una
variabile ed implementa un metodo init... (il designato) per inizializzare le proprie
variabili. Si definiscono inoltre inizializzatori secondari e si assicuri che gli inizializzatori
ereditati siano trascurati se necessario. La Figura 21 illustra gli inizializzatori delle tre
classi e le loro relazioni.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
143
Il designato di ogni classe è l'inizializzatore con maggiore copertura, il metodo che
inizializza gli attributi aggiunti dalla sottoclasse. Il designato è anche il metodo init... che
invoca tramite messaggio alla superclasse il suo metodo designato. Nell'esempio di Figura
21 il designato della classe C, initWithTitle:date:, invoca il designato della superclasse,
initWithTitle:, che a sua volta invoca il metodo init della classe A.
Gli inizializzatori secondari spesso trascurano le versioni di inizializzatori ereditate. La
Figura 20: Interazione tra inizializzatore designato e secondari
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
144
classe C, nell'esempio di Figura 21, sovrascrive initWithTitle: ed invoca il suo designato
passandogli un valore predefinito. Questo designato, a sua volta, invoca il designato della
classe B, che è il metodo sovrascritto initWithTitle:. Se si invia un messaggio
initWithTitle: ad un oggetto della classe B e classe C, bisogna invocare diverse
implementazioni di metodi. D'altro canto, se la classe C non sovrascrive initWithTitle: e si
invia un messaggio ad un'istanza della classe C, l'implementazione della classe B
dovrebbe essere invocata. Conseguentemente l'istanza C verrebbe inizializzata in maniera
incompleta in quanto manca un dato. Quando si crea una sottoclasse quindi bisogna
assicurarsi che tutti i metodi di inizializzazione siano collegati nella catena gerarchica.
In sintensi quindi, a volte il designato di una superclasse è sufficiente per la sottoclasse e
così risulta superfluo implementarne un altro. Altre volte invece il designato di una classe
deve ampliare le caratteristiche del designato della classe da cui deriva, così deve
sovrascriverlo.
4.4.6.3 I metodi dealloc e finalize
Nelle classi Cocoa che utilizzano garbage collection, il metodo finalize è il posto dove la
classe dispone di tutte le risorse rimanenti e degli allegati delle sue istanze prima che
queste vengano liberate. Nelle classi Cocoa che usano una gestione tradizionale della
memoria, il metodo comparabile per liberare le risorse è il metodo dealloc. Sebbene siano
simili ci sono differenze significative tra di essi.
Da molti punti di vista il metodo dealloc può essere considerato la controparte del metodo
init... della classe, in particolare del suo designato. Tale metodo è invocato esattamente
prima della distruzione dell'oggetto, garantendo che le istanze dell'oggetto vengano
rilasciate e che la memoria allocata dinamicamente venga liberata.
Il punto finale del parallelismo tra il metodo dealloc ed init... ha a che fare con
l'invocazione dell'implementazione dello stesso metodo della superclasse. Un
inizializzatore invoca il designato della superclasse come primo passo, dealloc invece
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
145
invoca il dealloc della superclasse solo alla fine. Di conseguenza la deallocazione
funziona in maniera simmetrica rispetto ai metodi init....
- (void)dealloc {
[accountDictionary release];
if ( mallocdChunk != NULL )
free(mallocdChunk);
[super dealloc];
}
Riquadro 14: Esempio di metodo dealloc
Bisogna notare, come nell'esempio del Riquadro 14, che è prudente verificare se la
variabile malloc è non-nulla prima di liberarla. Poiché non crea problemi inviare un
messaggio nullo ad un oggetto, questa precauzione non è necessaria per le istanze di
variabili.
Simile al metodo dealloc, il metodo finalize chiude le risorse utilizzate da un oggetto in un
ambiente garbage collection prima che un oggetto venga liberato e la sua memoria resa
disponibile. Come in dealloc l'ultima riga di un'implementazione di finalize dovrebbe
invocare l'implementazione del metodo della superclasse. Tuttavia, a differenza del
metodo dealloc, un'implementazione finalize non deve rilasciare le istanze di variabili
perchè garbage collection distrugge questi oggetti a tempo debito.
Ci sono comunque differenze significative tra questi metodi di “pulizia”. È
raccomandabile non implementare un metodo finalize e, se necessario, riferirlo a pochi
oggetti. Il primo motivo per non consigliare l'uso del finalize è che l'ordine in cui gli
oggetti garbage-collected sono inviati al finalize è indeterminato, anche se ci sono
collegamenti tra loro. Una conseguenza potenzialmente negativa di questa
indeterminazione è nel caso in cui stanno circolando messaggi tra gli oggetti che stanno
per essere “finalizzati”. Il codice infatti non può dipendere dagli effetti collaterali derivanti
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
146
dall'ordine di deallocazione degli oggetti. Bisognerebbe quindi costruire un codice nel
quale la liberazione di blocchi, la chiusura del descrittore di file e l'annullamento della
registrazione di osservatori avviene prima che sia invocato il metodo finalize.
4.4.6.4 Metodi costruttori di classe
Questi metodi combinano l'allocazione e l'inizializzazione in un unico blocco e
restituiscono un oggetto autorilasciato (in codice memory managed). Questi metodi sono
nella forma “+ (type)className...” dove className esclude ogni prefisso.
Cocoa fornisce diversi esempi, NSDate include il seguente metodo costruttore:
+ (id)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs;
+
(id)dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)secs;
+ (id)dateWithTimeIntervalSince1970:(NSTimeInterval)secs;
I metodi costruttori possono essere più di una semplice comodità perchè non solo possono
combinare allocazione ed inizializzazione, ma la prima può comunicare con la seconda.
Questo vuol dire che si potrebbe allocare memoria per esempio solo dopo che il processo
di inizializzazione ha verificato di quanta memoria si necessita.
Si consideri per esempio di dover inizializzare un oggetto collection da un file che codifica
un numero qualsiasi di elementi per collection (oggetti NSString, NSData, NSNumber e
così via). Prima che il metodo costruttore possa conoscere quanta memoria deve allocare
per la collezione, deve leggere prima il file ed analizzare la lista di proprietà per poi
determinare quanti elementi ci sono e di che tipo di oggetti si tratta. Un altro scopo per
l'utilizzo di metodi costruttori è garantire che una determinata classe offra una istanza
singleton. Anche se un metodo init... potrebbe verificare che esista solo un'istanza in
qualunque momento del programma, potrebbe però richiedere di allocare un' istanza
“grezza” che poi eventualmente verrà rilasciata se un'altra è già attiva. Un metodo
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
147
costruttore invece evita l'allocazione di un oggetto senza la certezza di doverlo
inizializzare. Nel Riquadro 15 è indicato un metodo costruttore per una istanza singleton.
static AccountManager *DefaultManager = nil;
+ (AccountManager *)defaultManager {
if (!DefaultManager) DefaultManager = [[self
allocWithZone:NULL] init];
return DefaultManager;
}
Riquadro 15: Un metodo costruttore per una istanza singleton
4.4.7 Introspection
Introspection è una caratteristica molto potente di ambienti e linguaggi orientati agli
oggetti. Questa caratteristica si riferisce alla capacità di un oggetto di divulgare
informazioni su sé stessi nella fase di runtime. Tali dettagli riguardano il posto nella catena
gerarchica, la conformità a determinati protocolli e le risposte a determinati messaggi. Il
protocollo e la classe NSObject definiscono molti metodi introspection che si possono
usare al tempo di esecuzione per caratterizzare gli oggetti.
4.4.7.1 Valutazione dei rapporti gerarchici
Per conoscere qualcosa di un oggetto, bisogna innanzitutto conoscere la classe a cui
appartiene, inoltre potrebbe servire conoscere le sue caratteristiche, gli attributi che
rappresenta ed a quali tipi di messaggi può rispondere. Attraverso l'introspection quindi si
acquisisce familiarità con la classe dell'oggetto e si conosce per esempio quali messaggi
non bisogna inviargli.
Il protocollo NSObject fornisce diversi metodi per determinare la posizione di un oggetto
nella gerarchia delle classi, tale metodo opera a diversi livelli di granularità. I metodi di
istanza class e superclass per esempio, restituiscono oggetti Class che rappresentano
rispettivamente la classe e la superclasse per il ricevitore. Questi metodi richiedono di
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
148
confrontare un oggetto Class con un altro. Il Riquadro 16 è un esempio descrittivo.
// ...
while ( id anObject = [objectEnumerator nextObject] ) {
if ( [self class] == [anObject superclass] ) {
// do something appropriate...
}
}
Riquadro 16: Utilizzo di metodi di class
A volte si utilizzano i metodi class e superclass per ottenere un ricevitore appropriato ad
una classe di messaggi.
Per verificare la classe di appartenenza di un oggetto bisogna inviargli un messaggio
isKindOfClass: oppure isMemberOfClass:. Il primo metodo restituisce conferma o meno
se il ricevitore è un'istanza di una determinata classe o di una classe da essa derivata. Un
messaggio isMemberOfClass:, d'altro canto, restituisce conferma se il ricevitore è
un'istanza di una specifica classe. Il metodo isKindOfClass: è generalmente più utilizzato,
in quanto da esso si può ricavare in una sola volta la gamma completa di messaggi da
poter inviare ad un oggetto. Si consideri il seguente frammento di codice:
if ([item isKindOfClass:[NSData class]]) {
const unsigned char *bytes = [item bytes];
unsigned int length = [item length];
// ...
}
Sapendo che l'oggetto item deriva dalla classe NSData, Il codice successivo sa di poter
inviare NSData byte ed il messaggio lenght. La differenza tra isKindOfClass: e
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
149
isMemberOfClass: diventa evidente se si assume che item è un'istanza di NSMutableData.
Se si usa isMemberOfClass: al posto di isKindOfClass:, il codice nel blocco non verrà mai
eseguito perchè item non è un'istanza di NSData ma di NSMutableData:, una sua
sottoclasse.
4.4.7.2 Implementazione di metodi e conformità ai protocolli
Due metodi introspection importanti di NSObject sono respondsToSelector: e
conformToProtocol:. Questi metodi restituiscono rispettivamente se un oggetto
implementa un determinato metodo oppure un oggetto è conforme ad un determinato
protocollo formale.
È possibile utilizzare questi metodi in situazioni simili. Consentono di scoprire se alcuni
oggetti anonimi potenzialmente rispondono adeguatamente ad un particolare messaggio o
una serie di messaggi, prima di inviargliene qualcuno. In questo modo sarà possibile
evitare eccezioni di runtime derivanti da selettori non riconosciuti. Application Kit
fornisce protocolli informali per verificare se i delegati attuano un metodo delegato
(utilizzando respondsToSelector:) prima del ricorso a tale metodo.
4.4.7.3 Comparazione di oggetti
Anche se non sono metodi strettamente introspection i metodi hash e isEqual: compiono
operazioni simili. Tali metodi sono indispensabili agli strumenti di runtime per identificare
e comparare oggetti, ma invece di interrogare il sistema di runtime per ricevere
informazioni sugli oggetti, si basano su comparazione logica tra classi specifiche.
I metodi hash ed isEqual:, dichiarati entrambi dal protocollo NSObject sono strettamente
connessi. Il metodo hash deve essere implementato per restituire un intero che può essere
usato come indirizzo di una tabella per una tabella hash strutturata. Se due oggetti sono
uguali (come determinato dal metodo isEqual:) devono avere lo stesso valore hash.
Il metodo isEqual: è estremamente semplice, confronta il ricevitore con l'oggetto passato
come argomento. La comparazione di oggetti spesso informa il sistema runtime sulle
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
150
decisioni da prendere e cosa si può fare con un oggetto, nel Riquadro 17 si usa isEqual:
per decidere che azione compiere.
- (void)saveDefaults {
NSDictionary *prefs = [self preferences];
if (![origValues isEqual:prefs])
[Preferences savePreferencesToDefaults:prefs];
}
Riquadro 17: Uso di isEqual
Se si sta creando una sottoclasse, potrebbe essere necessario sostituire isEqual: per
aggiungere ulteriori controlli sull'uguaglianza di oggetti. La sottoclasse potrebbe definire
infatti ulteriori attributi che potrebbero assumere lo stesso valore in due istanze diverse, in
modo tale da poter essere considerate uguali.
4.4.8 Oggetti modificabili
Gli oggetti Cocoa possono essere mutabili o immutabili. Non è possibile modificare i
valori incapsulati in oggetti immutabili, quando un tale oggetto viene creato il suo valore
rimane lo stesso per tutto il suo ciclo di vita. È possibile altresì cambiare il valore
incapsulato in un oggetto mutabile in qualunque momento.
Le impostazioni predefinite degli oggetti prevedono che questi siano mutabili. Molti
oggetti permettono di cambiare i loro valori incapsulati attraverso metodi accessori setter.
Per esempio è possibile cambiare le dimensioni, la posizione, il titolo ed altre
caratteristiche degli oggetti NSWindows.
Il framework Foundation aggiunge altre caratteristiche introducendo il concetto di classi
che hanno varianti mutabili e non mutabili. Le sottoclassi mutabili sono generalmente
sottoclassi di classi immutabili ed hanno il prefisso Mutable nel loro nome. Esempi di
queste classi possono essere NSMutableArray, NSMutableDictionary, NSMutableSet,
NSMutableURLRequest, NSMutableString ed altre.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
151
Salvo per NSMutableParagraphStyle in Application Kit, il framework Foundation
definisce sempre nel nome tutte le classi mutabili, ma in generale tutti i framework Cocoa
possono potenzialmente avere varianti mutabili o immutabili delle classi.
La necessità di avere la possibilità di rendere un oggetto immutabile risiede nel fatto che
rendere sempre un oggetto mutabile potrebbe portare a diversi problemi di coerenza di
dati. Ad esempio, nel caso in cui un utente stia visualizzando una matrice che popola una
tabella, se viene selezionata una riga di tale matrice potrebbe darsi che un'altra parte del
codice nel frattempo abbia modificato quei valori o quei riferimenti. Una tale situazione
genererebbe un problema nella visualizzazione di quei dati. Avere oggetti immutabili
quindi garantisce che un oggetto non cambi il suo valore in maniera inaspettata mentre lo
si sta usando.
Gli oggetti che si prestano a diventare immutabili sono gli oggetti che includono collezioni
di valori discreti o contengono valori registrati in buffer. Non tutti i valori comunque
beneficiano nell'avere una versione mutabile, un valore ad esempio che contiene un
singolo dato come una istanza di NSNumber o NSDate non è un buon candidato per essere
mutabile. Quando uno di questi valori cambia, ha molto più senso sostituire l'istanza con
una nuova piuttosto che cambiare il valore contenuto nell'attributo.
Le prestazioni sono un altro motivo per gestire versioni immutabili di oggetti come
collezioni e dizionari. tali oggetti, se mutabili, devono gestire dinamicamente il cambio di
locazione di memoria, la quantità di memoria da allocare e deallocare, quindi un oggetto
mutabile può risultare meno efficiente della sua controparte immutabile.
In teoria un oggetto immutabile garantisce che i suoi valori rimangano stabili, in pratica
però questa caratteristica non è sempre verificata. Un metodo può scegliere un oggetto
mutabile come tipo restituito da una sua variante immutabile, successivamente, potrebbe
decidere di modificare tale oggetto, eventualmente violando le assunzioni e le scelte fatte
dal destinatario sulla base del valore originario. Da tali considerazioni sono deducibili
alcuni approcci alla programmazione che ottimizzano l'utilizzo di oggetti mutabili. Ad
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
152
esempio è consigliabile utilizzare varianti mutabili di oggetti quando questi necessitano di
frequenti modifiche. Allo stesso tempo è consigliabile sostituire un oggetto immutabile
piuttosto che gestirne uno mutabile, se questo non richiede modifiche frequenti.
È da segnalare inoltre che molte classi mutabili offrono metodi costruttori ed
inizializzatori che permettono di specificare la probabile capacità iniziale dell'oggetto,
questo approccio rende più efficiente la memorizzazione di oggetti mutabili. È inoltre
possibile creare un oggetto mutabile creando una copia di un oggetto esistente dello stesso
tipo generale. Per fare ciò, bisogna invocare il metodo mutableCopy, implementato da
ogni superclasse immutabile di una classe mutabile di Foundation. É possibile inoltre
inviare una copia di un oggetto mutabile per creare una copia immutabile dello stesso.
In generale le domande da porsi nella scelta se utilizzare oggetti mutabili o immutabili
riguarda la natura delle variabili, se sono stringhe di caratteri, dizionari o oggetti che non
variano frequentemente nel tempo. Generalmente quando si ha un oggetto il cui contenuto
cambia in blocco è utile crearlo immutabile, Stringhe (NSString) ed oggetti di dati
(NSData) generalmente fanno parte di questa categoria. Altresì, se un oggetto si modifica
continuamente è giusto crearlo mutabile. Collezioni come array e dizionari fanno parte di
questa categoria, tuttavia la frequenza dei cambiamenti e le dimensioni della collezione
dovrebbe essere un fattore decisionale. Per esempio se si dispone di un oggetto che cambia
di rado è meglio renderlo immutabile.
Un'ulteriore considerazione da sottolineare riguarda il salvataggio di oggetti mutabili nelle
collezioni. Tali oggetti potrebbero infatti creare problemi, alcune collezioni potrebbero
essere invalidate o corrotte se gli oggetti contenuti mutano, in quanto la mutazione
potrebbe influire sul modo in cui tali oggetti sono inseriti nella collezione. Considerando
ad esempio oggetti NSDirectory e NSSet, la corruzione potrebbe riguardare la proprietà
delle chiavi hash. Se si modificano gli oggetti per esempio potrebbero influenzare il
risultato del valore hash o del metodo isEqual: e la collezione risulterebbe corrotta. Un
altro esempio potrebbe riguardare una collezione ordinata, come un array. Se cambiano le
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
153
sue proprietà, tale ordinamento potrebbe non essere più valido.
4.4.9 Raggruppamento di classi
Il raggruppamento di classi (detta anche Class cluster) è uno strumento che estende l'uso
del framework Foundation. Lo scopo che persegue è di raggruppare un certo numero di
sottoclassi, private e concrete sotto una superclasse pubblica e astratta. Tale
raggruppamento delle classi semplifica l'architettura pubblica visibile di un framework
orientato agli oggetti senza ridurne le potenzialità.
4.4.9.1 Struttura gerarchica
Per illustrare l'architettura dei raggruppamenti e dei suoi benefici, consideriamo il
problema di costruire una gerarchia che definisce gli oggetti che registrano numeri di
differenti tipologie (char, int, float, double). Poiché i numeri di diversi tipi hanno molte
caratteristiche in comune (per esempio possono essere convertiti da un tipo in un altro e
possono essere rappresentati da una stringa), possono essere osservati in una singola
classe. Comunque l'archiviazione di tali oggetti è differente, di conseguenza non è
efficiente rappresentarli con una sola classe. È possibile quindi considerare la seguente
architettura:
Number è la superclasse astratta che dichiara nei suoi metodi le operazioni comuni alle sue
sottoclassi, comunque non dichiara istanze di variabili per memorizzare numeri. Le
sottoclassi dichiarano tali variabili e condividono l'interfaccia di programmazione
Figura 21: Class Cluster
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
154
dichiarata da Number.
Possiamo quindi dedurre che gli utenti di questa gerarchia vedano solo una classe pubblica
ed astratta, Number. Il modo in cui è possibile istanziare oggetti dipende da come la classe
astratta gestisce le istanze.
4.4.9.2 Creare istanze
La superclasse astratta deve dichiarare metodi per creare istanze delle sue sottoclassi
private. È responsabilità della superclasse dispensare un oggetto della propria sottoclasse
basato su un metodo invocato. Non è possibile e non si deve scegliere la classe
dell'istanza.
Nel framework Foundation generalmente è possibile creare un oggetto invocandolo con
metodo di classe + className... o metodi init... e alloc....
Considerando la classe NSNumber come esempio, è possibile inviare i seguenti messaggi
per creare oggetti number:
NSNumber *aChar = [NSNumber numberWithChar:’a’];
NSNumber *anInt = [NSNumber numberWithInt:1];
NSNumber *aFloat = [NSNumber numberWithFloat:1.0];
NSNumber *aDouble = [NSNumber numberWithDouble:1.0];
In questo modo si creano oggetti che si deallocheranno automaticamente.
Ogni oggetto restituito (aChar, anInt, aFloat e aDouble) può appartenere a differenti
sottoclassi. Anche se la classe dell'oggetto è nascosta, la sua interfaccia è pubblica e viene
dall'interfaccia dichiarata dalla classe astratta NSNumber. Anche se non è propriamente
corretto conviene considerare gi oggetti aChar, anInt, aFloat, e aDouble come istanze
della classe NSNumber, da quando sono creati ed usati dai metodi della classe NSNumber.
4.4.9.3 Raggruppamento di classi con molteplici superclassi pubbliche
Nell'esempio del paragrafo 4.4.9.2. Una classe astratta e pubblica dichiara l'interfaccia per
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
155
molteplici sottoclassi private, questo è un esempio puro di raggruppamento di classi. È
inoltre possibile e spesso molto utile, avere due o più classi astratte pubbliche che
dichiarano l'interfaccia del raggruppamento. Questo è evidente nel Framework
Foundation, che include questi raggruppamenti:
Raggruppamento
di classi
Superclassi pubbliche
NSData NSData
NSMutableData
NSArray NSArray
NSMutableArray
NSDictionary NSDictionary
NSMutableDicotionary
NSString NSString
NSMutabileString
Tabella 3: Raggruppamenti di classi
Esistono comunque altri raggruppamenti di tipi, ma quelli in tabella 3 illustrano come
cooperano nella dichiarazione dell'interfaccia di programmazione di un raggruppamento di
classi. In ognuno di questi raggruppamenti, un nodo pubblico dichiara metodi ai quali
possono rispondere tutti gli oggetti, l'altro nodo dichiara metodi che sono appropriati solo
per gruppi di oggetti che possono far modificare il proprio contenuto.
Questo tipo di costruzione delle interfacce permette di rendere l'interfaccia di
programmazione di un framework molto più espressiva. Ad esempio si consideri un
oggetto Book che dichiara questo metodo:
- (NSString *)title;
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
156
L'oggetto Book potrebbe restituire sia una sua istanza che creare una nuova stringa. È
chiaro però da questa dichiarazione che la stringa restituita non può essere modificata
senza generare un warning dal compilatore.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
157
Capitolo 5 Presentazione di un caso di studio Il seguente capitolo descrive eat&Phone, un software sviluppato all'occorrenza come caso
di studio. Tale applicazione risalta le caratteristiche hardware integrate nel dispositivo
mobile, utilizzando gli strumenti di sviluppo messi a disposizione da Apple inc. In
particolare ci si è concentrati sull'utilizzo del GPS16 e della possibilità di interagire con il
web attraverso il browser Safari.
I requisiti di tale software richiedono infatti di poter determinare le coordinate
geografiche. Una volta determinate tali coordinate è richiesto al software di interagire con
un sito web, navigabile in Safari, per determinare i ristoranti in prossimità della locazione
determinata.
I requisiti esposti permettono in un primo momento di interagire con il Core locatio per
determinare le coordinate geografiche; successivamente con le altre applicazioni istallate
nel dispositivo, in particolare con il Browser Safari.
Bisogna notare inoltre, che tutti gli strumenti messi a disposizione da Apple permettono di
costruire interfacce utente in modo facile e sopratutto conservando quelle caratteristiche di
usabilità tipiche delle applicazioni per Mac OS X ed iPhone OS. È interesse della casa
produttrice quindi riutilizzare, anche nelle applicazioni di terze parti, quelle caratteristiche
estetiche e di usabilità che permettano all'utente di riconoscere determinate funzioni
proprio perchè comuni a tutte le applicazioni dell'ambiente di utilizzo.
5.1 Progettazione
Il paradigma di progettazione delle applicazioni per iPhone segue una struttura di tre
elementi principali: Model, controller e View. Il collegamento tra questi elementi è
indicato nella Figura 23.
16 Si noti che il GPS integrato non è l'unico modo per ricavare le coordinate geografiche. Il dispositivo infatti le ricava
anche dalla triangolazione delle celle e dal collegamento internet, a differenti livelli di precisione.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
158
In tale struttura quindi è il controller che gestisce la comunicazione tra la base di dati e le
View dell'interfaccia utente. Nel seguito verrà descritto come questo approccio è stato
applicato nel software utilizzato come caso d'uso.
Per realizzare eat&Phone è stato necessario far riferimento al framework Location, tale
software infatti utilizza la classe CLLocationManager per determinare la posizione
geografica. Nei capitoli precedenti si è visto come Apple fornisca un ambiente di sviluppo
integrato nel quale è possibile sviluppare l'applicazione. Per lo sviluppo di eat&Phone si è
utilizzato Xcode 3.1, Interface Builder 3.1.2 ed iPhone Simulator 2.1.
Le classi implementate in eat&Phone sono le seguenti:
• MainViewController, la cui superclasse è UIViewController. Tale classe adotta il
protocollo MyCLControllerDelegate dichiarato in MyCLController.h
• eat&PhoneAppDelegate, la cui superclasse è NSObject. Tale classe adotta il protocollo
UIApplicationDelegate della classe UIApplication del framework UIKit.
• MyCLController, la cui superclasse è NSObject. Tale classe adotta il protocollo
Figura 22: Paradigma di progettazione
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
159
CLLocationManagerDelegate dichiarato in CLLocationManagerDelegate.h, del
framework Core Location.
Considerando il paradigma di programmazione che fa riferimento allo schema Model-
Control-View è possibile considerare la classe MainViewController come fusione tra il
Model ed il Control. Tale costruzione viene dal fatto che il Model rappresenta la struttura
dati da cui prelevare le informazioni per inviarle all'interfaccia grafica
(MainWindows.xib). Nel caso in esame invece non esiste un vera e propria struttura dati,
ma una comunicazione attraverso messaggi tra le classi implementate ed il Core Location.
La classe che fa da Controller è MainViewController, che utilizza il protocollo
MyCLControllerDelegate per comunicare con MyCLController. MainViewcontroller è
infatti il suo delegato. MyCLController invece è il delegato di Core Location, riceve
aggiornamenti sulle coordinate geografiche attraverso il protocollo adottato
CLLocationManagerDelegate. MainViewController, ricevuti gli aggiornamenti sulle
coordinate e sulla stringa da inviare a Safari, aggiorna l'interfaccia grafica.
La classe MainViewController oltre a mostrare sul display la posizione gps, verifica la
disponibilità del LocationService usando la proprietà locationServicesEnabled della classe
CLLocationManager.
Il diagramma delle classi è rappresentato nella seguente figura:
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
160
Di seguito sono descritte le classi implementate:
Nome: MainViewController
Gestisce l'interfaccia principale nella quale si visualizzano i dati ricavati
dal GPS ed i pulsanti di avvio della ricerca delle coordinate e di
collegamento al browser. È inoltre il delegato di MyCLController e quindi
riceve da esso gli agigornamenti
Attributi dichiarati nell'interfaccia:
Figura 23: Diagramma delle Classi
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
161
<updateTextView>:<IBOutlet UITextView>
<startStopButton>:<IBOutlet UIBarButtonItem>
<launchBrowser>:<IBOutlet UIBarButtonItem>
<spinner>:<IBOutlet UIActivityIndicatorView>
<isCurrentlyUpdating>:<BOOL>
Metodi dichiarati nell'interfaccia:
<startStopButtonPressed>:<IBAction>
<launchButtonPressed>:<IBAction>
Protocolli adottati:
MyCLControllerDelegate
Nome: eatPhoneAppDelegate
Delegato dell'applicazione, crea la finestra dell'applicazione e la View
principale.
Attributi dichiarati nell'interfaccia:
<window>:<IBOutlet UIWindow>
<mainViewController>:<IBOutlet MainViewController>
Protocolli adottati:
UIApplicationDelegate
Nome:MyCLControllerDelegate
Classe Singleton delegato per ricevere da CoreLocation le coordinate.
Attributi dichiarati nell'interfaccia:
<locationManager>:<CLLocationManager>
<delegate>:<id>
<locURL>:<NSMutableString>
Protocolli dichiarati nell'interfaccia:
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
162
<MyCLControllerDelegate>:<NSObject>
Metodi dichiarati nell'interfaccia:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error;
+ (MyCLController *)sharedInstance;
Protocolli adottati:
CLLocationManagerDelegate
Come descritto nei capitoli precedenti Objective-C è un linguaggio estremamente basato
sullo scambio di messaggi. Le informazioni scambiate tra le classi transitano quindi
attraverso messaggi. Il flusso di tali informazioni avviene nel seguente modo:
• Il delegato dell'applicazione (eat&PhoneAppDelegate) crea una finestra ed inizializza
un oggetto MainViewController.
• MainViewController attende input dall'utente fino a che non lo riceve tramite il tasto
StartStopButton.
• Tale input invoca MCLController attraverso il protocollo
CLLocationManagerDelegate.
• MCLController, delegato del Core Location, restituisce il risultato a
MainViewController che lo invia alla view dell'interfaccia grafica.
• Una volta ricevuto l'aggiornamento dal Core Location attraverso MCLController,
MainViewController è pronto a ricevere anche un altro input tramite il tasto “Trova
Ristorante” (launchButtonPressed), in modo da inviare un stringa come URL al
Browser Safari.
• L'applicazione si chiude automaticamente nel momento in cui si avvia il Browser.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
163
È possibile osservare il Collaboration Diagram delle classi nella seguente figura:
5.2 Implementazione
Per implementare eat&Phone si è utilizzato come base la funzione main generata
automaticamente da Xcode per le applicazioni Cocoa. L'ambiente di sviluppo infatti, nel
momento in cui si crea un nuovo progetto, costruisce una funzione main elementare con
una interfaccia grafica vuota. Tale procedura permette di avere già un'applicazione
eseguibile, pur non avendo ancora implementato nessuna caratteristica.
Si analizzino ora le implementazioni delle singole classi.
5.2.1 eat&PhoneAppDelegate
L'interfaccia di eat&PhoneAppDelegate è contenuta nel file eat&PhoneAppDelegate.h.
Nella sua dichiarazione viene richiamata la classe MainViewController in quanto dichiara
una variabile di istanza MainViewController. Il richiamo avviene attraverso la direttiva:
Figura 24: Collaboration Diagram
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
164
@class MainViewController;
Nell'interfaccia inoltre viene dichiarata l'adozione del protocollo UIApplicationDelegate.
Tale protocollo dichiara metodi implementati dal delegato dell'oggetto singelton
UIApplication. Implementando questi metodi, il delegato può rispondere al lancio ed alla
chiusura dell'applicazione, ai warning di memoria insufficiente, all'apertura di risorse URL
ed altri eventi di sistema.
L'implementazione di tale classe è realizzata nel file eat&PhoneAppDelegate.m. Viene
implementato al suo interno il solo metodo opzionale applicationDidFinishLaunching:
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
[window addSubview:[mainViewController view]];
[window makeKeyAndVisible];
Tale metodo, dichiarato nella classe UIApplication.h, indica al delegato quando
l'applicazione ha terminato l'avvio. È utilizzato in genere per inizializzare attraverso il
delegato la configurazione iniziale dell'applicazione, crea la finestra principale ed un
oggetto mainViewController. È utile per ripristinare l'applicazione allo stato precedente,
invia inoltre una notifica UIApplicationDidFinishLaunchingNotification contestualmente
alla chiamata di tale metodo.
5.2.2 MainViewController
MainViewController dichiara la propria interfaccia in MainViewController.h. All'interno
di questa, adotta il protocollo MyCLControllerDelegate, con il quale riceve aggiornamenti
da MyCLController. Tale protocollo è dichiarato nell'interfaccia di MyCLController. Le
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
165
istanze di variabili implementate sono quindi gli elementi della View, l'interfaccia grafica
creata con Interface Builder. Nello specifico sono dichiarati:
• updateTextView. Il campo di testo da aggiornare attraverso il protocollo
MyCLControllerDelegate
• startStopButton. il tasto che avvia la comunicazione con MyCLController per
ricercare le coordinate GPS
• launchBrowser. il tasto che avvia il browser Safari, inviandogli la stringa come URL
da aprire
• spinner. Indica l'avvio della ricerca delle coordinate geografiche
Inoltre è dichiarata una variabile booleana che indica se si sta effettuando una ricerca o
meno. Tale variabile serve per disabilitare il tasto di lancio del Browser.
I metodi pubblici dichiarati sono quelli relativi alla pressione del tasto startStopButton e
del tasto di lancio del Browser.
La dichiarazione dell'interfaccia è la seguente:
#import "MyCLController.h"
@interface MainViewController : UIViewController
<MyCLControllerDelegate> {
IBOutlet UITextView *updateTextView;
IBOutlet UIBarButtonItem *startStopButton;
IBOutlet UIBarButtonItem *launchBrowser;
IBOutlet UIActivityIndicatorView *spinner;
BOOL isCurrentlyUpdating;
}
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
166
//permettono di attribuire proprietà di non atomicità
// e di ratain alle variabili di istanza dichiate.
@property (nonatomic, retain) UITextView *updateTextView;
@property (nonatomic, retain) UIBarButtonItem *startStopButton;
@property (nonatomic, retain) UIBarButtonItem *launchBrowser;
@property (nonatomic, retain) UIActivityIndicatorView *spinner;
- (IBAction)startStopButtonPressed:(id)sender;
- (IBAction)launchButtonPressed:(id)sender;
@end
L'implementazione di tale classe è strutturata in MainViewController.m. Innanzitutto
vengono attribuiti i metodi accessori alle variabili di istanza attraverso la direttiva
@synthesize. Successivamente si implementa il primo e fondamentale metodo,
initWithNibName.
// Il primo metodo implementato, inizializza l'applicazione
- (id)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle*)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil
bundle:nibBundleOrNil]) {
isCurrentlyUpdating = NO;
[launchBrowser setEnabled:NO];
}
return self;
}
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
167
Tale metodo è fondamentale in quanto restituisce l'oggetto ViewController inizializzato
con il nib file17 del progetto. I parametri di tale metodo di istanza sono:
• nibName. È il nome del nib file senza informazioni del path di origine. Per definire
particolari valori del nib file è necessario sovrascrivere il metodo viewDidLoad
(descritto di seguito).
• NibBundle. È il path di riferimento per il nib file del progetto che si sta implementando.
Il parametro restituito da initWithNibName è quindi un oggetto UIViewController
inizializzato.
Il metodo viewDidLoad è invece sovrascritto nel seguente modo:
// è invocato solo quando la view è caricata per la prima
volta
- (void)viewDidLoad {
[startStopButton
setTitle:NSLocalizedString(@"StartButton", @"Start")];
//crea una istanza MyCLController e si attribuisce la sua
delega.
[MyCLController sharedInstance].delegate = self;
//verifica se l'utente ha disabilitato i location services,
nel qual
//caso disabilita il tasto start e visualizza un messaggio
nella view
17 La descrizione del Nib File è al paragrafo “Interface Builder” 3.2.4
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
168
if ( ! [MyCLController
sharedInstance].locationManager.locationServicesEnabled ) {
[self addTextToLog:NSLocalizedString(@"NoLocationServices",
@"User disabled location services")];
startStopButton.enabled = NO;
}
}
Riquadro 4: Metodo ViewDidLoad
Tale metodo void è implementato dalla classe UIViewController ed è invocato
immediatamente dopo il caricamento della view. Al suo interno si inizializza un oggetto
MyCLController attraverso il metodo di classe sharedInstance, implementato in
MyCLController.m. Tale metodo restituisce una istanza Singleton. Inoltre, attraverso il
messaggio “delegate = self”, MainViewController si dichiara come delegato per
l'istanza appena creata. All'interno di tale metodo si effettua anche il controllo
sull'eventuale disabilitazione da parte dell'utente dei location services.
Il metodo pubblico che riguarda l'azione relativa al tasto startStopButton è la seguente:
// Invocato alla pressione del tasto Start
-(IBAction)startStopButtonPressed:(id)sender {
if (isCurrentlyUpdating) {
[[MyCLController sharedInstance].locationManager
stopUpdatingLocation];
isCurrentlyUpdating = NO;
[startStopButton
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
169
setTitle:NSLocalizedString(@"StartButton", @"Start")];
[spinner stopAnimating];
[launchBrowser setEnabled:YES];
} else {
[launchBrowser setEnabled:NO];
[[MyCLController
sharedInstance].locationManagerstartUpdatingLocation];
isCurrentlyUpdating = YES;
[startStopButton
setTitle:NSLocalizedString(@"StopButton", @"Stop")];
[spinner startAnimating];
}
}
Il metodo startStopButtonPressed restituisce un valore di tipo IBAction. Tale valore viene
usato da Interface Builder per sincronizzare le azioni implementate nel codice con la sua
lista interna di azioni. Tale metodo verifica innanzitutto se è in corso una ricerca di
coordinate GPS attraverso la variabile booleana isCurrentlyUpdating. Nel caso verifica
questa condizione abilita attraverso il tasto startStopButton l'interruzione della ricerca,
altrimenti l'avvia attraverso lo stesso tasto.
I principali metodi richiamati in startStopButtonPressed sono i seguenti:
• stopUpdatingLocation. Metodo della classe CLLocationManager.h del framework
Core Location. È invocato alla pressione del tasto startStopButton, durante la ricerca
delle coordinate GPS.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
170
• startUpdatingLocation. Metodo della classe CLLocationManager.h del framework
Core Location. È invocato alla pressione del tasto startStopButton, per avviare la
ricerca di coordinate GPS.
• StartAnimating. Metodo della classe UIActivityIndicatorView ereditata da UIView nel
framework UIKit. È invocato tramite messaggio per l'animazione dell'oggetto spinner,
serve ad indicare che una ricerca di coordinate è in corso.
• StopAnimating. Metodo della classe UIActivityIndicatorView ereditata da UIView nel
framework UIKit. È invocato tramite messaggio per l'interruzione dell'animazione
dell'oggetto spinner.
Il metodo che invece lancia il browser attraverso l'azione legata al tasto LaunchButton è il
seguente:
// invocato alla pressione del tasto di ricerca del ristorante
-(IBAction)launchButtonPressed:(id)sender {
if ((!isCurrentlyUpdating)) {
[[UIApplication sharedApplication] openURL:[NSURL
URLWithString:[MyCLController sharedInstance].locURL]];
}
}
Tale metodo prima verifica se si sta effettuando una ricerca di coordinate, nel qual caso
inibisce la l'azione di lancio del Browser. Se invece la ricerca è finita e le coordinate GPS
sono state ricavate allora viene attivato il tasto launchBrowser.
All'interno del metodo si invia una stringa a Safari che contiene il link da aprire. Questa
operazione avviene attraverso messaggi innestati descritti di seguito:
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
171
• Si crea una istanza singleton della classe UIApplication (del framework UIKit)
attraverso il metodo sharedApplication
• si invoca il metodo openURL: sempre di UIApplication. Tale metodo accetta in
ingresso l'url creato a sua volta da URLWithString:, della classe NSURL del framework
Foundation. Tale metodo converte in url una stringa che gli viene passata come
parametro. La stringa utilizzata è locURL, ricavata dall'istanza singelton
MyCLController.
Si noti che il metodo openURL: apre l'applicazione predefinita dal sistema per aprire un
url. Inoltre tale metodo genera la chiusura dell'applicazione che lo richiama, nel caso in
esame di eat&Phone.
Nel seguito dell'implementazione della classe sono implementati i metodi privati
trasparenti all'utente ed i metodi delegati per la classe MyCLController:
• shouldAutorotateToInterfaceOrientation:. Tale metodo è dichiarato nella classe
UIViewController. Restituisce un valore booleano che indica quando il view controller
ruota automaticamente la view.
• didReceiveMemoryWarning. Tale metodo è dichiarato nella classe
UIApplicationDelegate. È un metodo opzionale e void, comunica al delegato quando
l'applicazione riceve dal sistema un warning per memoria insufficiente.
5.2.3 MyCLController
MyCLController è una classe singleton usata per comunicare con il framework Core
Location ed inviare gli aggiornamenti alla View, attraverso MainViewController.
L'interfaccia di MyCLController è dichiarata all'interno del file MyCLController.h,
implementata come nel Riquadro seguente:
// Questo protocollo è usato per inviare il testo ala View
// che contiene gli aggiornanti sulle coordinate GPS
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
172
@protocol MyCLControllerDelegate <NSObject>
@required
-(void)newLocationUpdate:(NSString *)text;
-(void)newError:(NSString *)text;
@end
// Definizione della classe
@interface MyCLController : NSObject <CLLocationManagerDelegate>
{
CLLocationManager *locationManager;
id delegate;
NSMutableString *locURL;
}
@property (nonatomic, retain) CLLocationManager *locationManager;
@property (nonatomic, assign) id <MyCLControllerDelegate>
delegate;
@property (nonatomic, retain) NSMutableString *locURL;
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation;
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error;
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
173
+ (MyCLController *)sharedInstance;
@end
Si noti che la prima dichiarazione riguarda il protocollo MyCLControllerDelegate. Tale
protocollo, come si è detto, permette la comunicazione a MainViewController degli
aggiornamenti sulle coordinate GPS ricevute da Core Location. Prevede
l'implementazione di due metodi obbligatori:
• newLocationUpdate. Necessario al passaggio della stringa di testo da inviare alla view
• newError. Per comunicare a MainViewController eventuali errori.
L'interfaccia della classe invece dichiara la superclasse (NSObject) e la conformità al
protocollo opzionale CLLocationManagerDelegate. Tale protocollo fa parte del
framework Core Location ed è dichiarata in CLLocationManagerDelegate.h.
CLLocationManagerDelegate definisce i metodi usati per ricevere aggiornamenti sulla
locazione da un oggetto CLLocationManager. Tra le variabili di istanze quindi è
inizializzata una istanza CLLocationManager.
Sono inizializzate inoltre un oggetto generico id ed una stringa (locURL), che verrà
utilizzata per passare a Safari l'indirizzo URL da aprire.
In conclusione vengono dichiarati due metodi di istanza pubblici (per la gestione delle
coordinate GPS e per la gestione di errori) ed un metodo di classe, per l'inizializzazione di
una istanza singleton (richiamato da MainViewController).
L'implementazione di questa classe è descritta in MyCLController.m. In tale file, prima
dell'implementazione della classe, viene dichiarata una istanza singleton MyCLController,
inizializzata al valore nullo. Tale istanza è necessaria all'implementazione del metodo di
classe sharedistance. La sua dichiarazione è:
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
174
static MyCLController *sharedCLDelegate = nil;
Il primo metodo implementato nella classe è il metodo init:
- (id) init {
self = [super init];
if (self != nil) {
self.locationManager = [[[CLLocationManager alloc]
init] autorelease];
// Comunica al location manager di inviare aggiornamenti a
questo oggetto
self.locationManager.delegate = self;
self.locURL = [[[NSMutableString alloc] init] autorelease];
}
return self;
}
Come si può notare la prima operazione invoca il metodo init per la superclasse, nel caso
non restituisca valore nullo procede nel seguente modo:
• alloca ed inizializza un oggetto CLLocationManager, lo associa così alla variabile
locationManager.
• Si definisce delegato di locationManager, così da ricevere gli aggiornamenti da core
location sulla posizione GPS.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
175
• Alloca ed inizializza una stringa LocURL di tipo mutabile.
La seconda implementazione riguarda il metodo pubblico che aggiorna le coordinate GPS
ed il timestamp. La sua prima parte è descritta nel seguente Riquadro:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
NSMutableString *update = [[[NSMutableString alloc] init]
autorelease];
// Timestamp
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc]
init] autorelease];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
[update appendFormat:@"%@\n\n", [dateFormatter
stringFromDate:newLocation.timestamp]];
Per definire il timestamp il metodo innanzitutto alloca ed inizializza una variabile della
classe NSDateFormatter, dateFormatter. Dopo avergli attribuito la formattazione
MediumStyle a data ed orario invia una sequenza di messaggi innestati, per:
• prelevare attraverso il metodo timestamp (di CLLocation) la data e l'orario usandoli
come attributi del metodo stringFromDate. Ottenendo una stringa data.
• Associate alla variabile update tale stringa gli si aggiungono successivamente i caratteri
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
176
speciali per inviare il testo a capo18 (\n\n).
Tale metodo prosegue con la ricerca delle coordinate GPS nel seguente modo:
// Coordinate orizzontali
if (signbit(newLocation.horizontalAccuracy)) {
// accuratezza negatvo indica un errore di rilevazone
//o misure non calcolabili
[update appendString:LocStr(@"LatLongUnavailable")];
} else {
// CoreLocation restituisce valori positivi per North & East,
negativi
// per South e West
// si costruisce la stringa inviandogli 4 argomenti
[update appendFormat:LocStr(@"LatLongFormat"),
fabs(newLocation.coordinate.latitude),
signbit(newLocation.coordinate.latitude) ?
LocStr(@"Sud") : LocStr(@"Nord"),
fabs(newLocation.coordinate.longitude),
signbit(newLocation.coordinate.longitude) ?
LocStr(@"Ovest") : LocStr(@"Est")];
[update appendString:@"\n"];
[update
appendFormat:LocStr(@"MeterAccuracyFormat"),
newLocation.horizontalAccuracy];
18 I caratteri aggiunti in coda servono, una volta inviata la stringa alla View, per distanziare il testo del timestamp dalle
coordinate geografiche.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
177
Innanzitutto viene verificato che le coordinate siano attendibili. La verifica avviene
attraverso il segno delle coordinate orizzontali. HorizontalAccuracy è dichiarato in
CLLocation.h, se negativo indica che la latitudine e la longitudine delle coordinate sono
errate. Se dovesse verificare un errore verrebbe restituito alla View la stringa
corrispondente a LatLogUnvailable19. Se invece dovesse risultare corretto il segno di
horizontalAccuracy, si aggiunge in coda alla variabile update la stringa creata
successivamente. Tale stringa viene costruita attraverso coordinate, che racchiude
informazioni sulle coordinate geografiche. Attraverso LocStr quindi si aggiungono ad
update informazioni sui punti cardinali e sull'accuratezza estrapolata da
horizontalAccuracy. In tal modo update contiene informazioni sul time stamp, le
informazioni geografiche e di precisione nella rilevazione.
Il Riquadro seguente invece utilizza le stesse procedure per formattare la stringa dell'URL
da inviare a Safari.
//URLCord ristampa le coordinate nel modo in cui vengono salvate
prima //dell'elaborazione e costruisce la stringa che deve essere
inviata come url a Safari.
[self.locURL appendFormat:LocStr(@"UrlString"),
fabs(newLocation.coordinate.latitude),
signbit(newLocation.coordinate.latitude) ?
LocStr(@"S+log=") : LocStr(@"N+long="),
19 Tale stringa è dichiarata nel file Localizable.strings e è richiamata dalla variabile LocStr. Tale variabile è stata
definita in MyCLController.m con la sintassi: #define LocStr(key) [[NSBundle mainBundle]
localizedStringForKey:(key) value:@"" table:nil].
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
178
fabs(newLocation.coordinate.longitude),
signbit(newLocation.coordinate.longitude) ?
LocStr(@"O") : LocStr(@"E")];
}
[update appendString:@"\n\n"];
// Invia gli aggiornamenti al delegato che nel caso in
esame è
//MainViewController
[self.delegate newLocationUpdate:update];
}
L'ultima operazione del metodo invia due messaggi innestati. Il primo invia update alla
variabile newLocationUpdate, questa poi viene inviata al proprio delegato:
MainViewController.
L'implementazione del metodo di classe dichiarato nell'interfaccia è la seguente:
// crea una istanza singleton
+ (MyCLController *)sharedInstance {
@synchronized(self) {
if (sharedCLDelegate == nil) {
[[self alloc] init]; // l'assegnazione non è
fatta qui
}
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
179
}
return sharedCLDelegate;
}
Tale metodo di classe restituisce sharedCLDelegate, una istanza singleton. In sostanza si
assicura dell'unicità dell'oggetto MyCLController.
5.3 Snapshot dell'applicazione
In figura 26 è visualizzata l'icona dell'iPhone sul display del simulatore.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
180
Figura 25: Icona di eat&Phone sul simulatore
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
181
In Figura 27 è presente l'interfaccia di eat&Phone al suo lancio.
Figura 26: lancio di eat&Phone
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
182
In Figura 28 eat&Phone ha ricavato le coordinate geografiche.
Figura 27: Visualizzazione delle coordinate geografiche
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
183
L'ultima immagine, Figura 29, visualizza invece i dati ottenuti e visualizzati in Safari.
Figura 28: Visualizzazione dei dati in Safari
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
184
Conclusioni Nei capitoli affrontati nel presente lavoro di tesi, si è analizzata la piattaforma software
iPhone, il dispositivo mobile promosso da casa Apple. Dopo un primo confronto
dell'approccio commerciale offerto per l'iPhone rispetto a prodotti concorrenti, ci si è
soffermati sulle sue caratteristiche principali. In particolare si è approfondita la struttura
del sistema operativo e dei framework implementati. L'analisi dei framework inoltre, come
per il sistema operativo, è stata effettuata in comparazione con quelli implementati nel
sistema Mac OS X 10.5, quindi nella sua ultima versione. Il sistema operativo desktop è
infatti la piattaforma da cui deriva. Le semplificazioni apportate riguardano ad esempio gli
strumenti di gestione della memoria per le applicazioni, per esigenze prestazionali iPhone
OS non supporta Garbage Collection. Inoltre è stato alleggerito di tutti i driver relativi a
periferiche esterne ed all'accesso da riga di comando, tipico di un ambiente BSD. Tale
analisi ha evidenziato inoltre come la Apple renda l'ambiente Cocoa estremamente
integrato agli strumenti di sviluppo che offre ed alle caratteristiche principali dell' iPhone
stesso. La filosofia “Apple“ infatti si riflette in ogni suo prodotto. La casa produttrice
orienta gli sviluppatori a porre l'attenzione più sull'interfaccia grafica da offrire all'utente
che alla stesura di codice. Anche nella documentazione è infatti consigliato l'uso degli
strumenti di sviluppo da loro distribuiti e delle interfacce grafiche offerte dei framework.
Tale approccio di coerenza grafica tra tutti i software della piattaforma restituisce
familiarità all'utente che li utilizza, punto di forza dei prodotti Apple.
I framework implementati infatti permettono di accedere a tutte le funzionalità del
dispositivo, ma anche di usufruire in maniera immediata e semplice delle caratteristiche
funzionali che hanno reso famoso l'iPhone. Tali framework infatti implementano ad
esempio le animazioni per la ricerca in elenchi o la gestione delle fotografie. Sono proprio
tali animazioni, estremamente curate nei dettagli, che permettono in poco tempo di
costruire un'applicazione accattivante ed usabile, coerente con tutte le altre applicazioni. È
importante notare che l'iPhone non gode di tutte le caratteristiche hardware di altri
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
185
dispositivi mobili concorrenti, in quanto ad esempio non può registrare filmanti o inviare
MMS, la sua stessa fotocamera ha una risoluzione molto inferiore rispetto ad altri
dispositivi. Nonostante ciò è uno dei prodotti più ricercati. Tale successo è dovuto proprio
alla grande usabilità che offre, rendendolo realmente un oggetto con cui poter navigare in
rete, riprodurre un video o scorrere le foto in maniera estremamente agevole. Insieme poi
ad un elegante design è comprensibile il successo di mercato.
Altra notazione importante riguarda gli strumenti di sviluppo e le applicazioni che la
Apple offre per ottimizzare i software sviluppati. Xcode è un ambiente di sviluppo che
ingloba caratteristiche comuni ad altri ambienti comparabili, come il completamento
automatico del testo o la divisione di un progetto in cartelle di gestione dei file. Quello che
lo caratterizza è invece l'integrazione con Interface Builder per generare interfacce di
utente e con i software per l'ottimizzazione delle prestazioni.
Essendo l'iPhone un dispositivo mobile, le sue caratteristiche hardware sono ridotte e
quindi bisogna ottimizzare per esempio l'uso della memoria e la gestione dell'allocazione
di oggetti nelle applicazioni. A causa di tali limitazioni, sull'iPhone non è possibile la
gestione della memoria Garbage Collection realizzabile su Mac OS X, bisogna quindi
gestire in maniera più semplice gli oggetti allocati e deallocati. La ridotta capacità
hardware però rende questa gestione estremamente delicata, come in tutti i dispositivi
mobili. Nello sviluppo di un'applicazione infatti bisogna tenere conto degli errori di
allocazione di memoria perchè generano rallentamenti ed invecchiamento del software
stesso. Se tali problemi in un sistema desktop sono spesso trascurabili, in un sistema
mobile non lo sono. Apple offre uno strumento semplice per verificare la correttezza di
codice nell'allocazione di oggetti, monitorando l'esecuzione del programma sviluppato. È
infatti noto che riscontrare in fase di debug tali problemi è estremamente complicato. Ad
esempio, attraverso tali software, si è potuto verificare agevolmente la correttezza del caso
di studio implementato nell'ultimo capitolo. Non sono stati riscontrati infatti oggetti
allocati in un primo momento e non deallocati successivamente.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
186
Altro strumento di supporto è iPhone Simulator. Tale software permette di testare
l'applicazione prima di procedere alla sua distribuzione. Bisogna notare che se non si
accede al programma di sviluppo iPhone e quindi non si paga la quota annua prevista, non
si può testare l'applicazione su nessun dispositivo. Tale limitazione non è trascurabile, in
quanto il simulatore non è del tutto attendibile. Se risulta sufficiente per applicazioni
semplici, lo stesso non si può dire per applicazioni più articolate. Bisogna infatti
sottolineare che tale simulatore non permette di verificare le prestazioni del software in
maniera attendibile in quanto i programmi vanno in esecuzione sull'hardware del desktop
sul quale si sviluppa, estremamente più efficiente di quello del dispositivo. Inoltre nel
simulatore non è possibile testare applicazioni che prevedono caratteristiche essenziali,
come il cambio di orientamento da orizzontale a verticale del dispositivo. Non è inoltre
possibile simulare in alcun modo eventi multitouch. Tale limitazione rende quindi
vincolante per il test definitivo di un'applicazione l'adesione al programma Apple,
vincolando ancor più sviluppatori di terze parti al “mondo” Apple. Tale approccio infatti è
riscontrabile in tutte le fasi di sviluppo e pubblicazione del software. Per poter anche
scaricare l'SDK è necessario registrarsi al programma di sviluppo, anche se questa volta in
maniera gratuita. Per poter invece pubblicare un software, anche solo gratuitamente,
bisogna ottenere la vidimazione della Apple. È possibile quindi notare un secondo vincolo.
Oltre al test finale che risulta di fatto a pagamento (necessario tra l'altro per la diffusione
dell'applicazione), si è sottoposti al vincolo di vidimazione, rischiando di vanificare così
gli investimenti nello sviluppo. Il terzo vincolo posto dalla casa produttrice è che l'unico
canale di distribuzione delle applicazioni è lo store on line, gestito sempre dalla Apple.
La Apple in sostanza mantiene il completo controllo del codice della piattaforma, degli
strumenti di sviluppo distribuiti ed allo stesso tempo del software pubblicato sulla
piattaforma da essa stessa gestita. Se da un lato questa “filosofia” commerciale ha sempre
contraddistinto Apple, dall'altro bisogna notare come altri soggetti del settore abbiano
scelto approcci in direzione decisamente contraria. Come si è evidenziato nel primo
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
187
capitolo, il mercato del software per i dispositivi mobili sta investendo molto
nell'approccio collaborativo e nella distribuzione di software sotto licenze sempre meno
restrittive. L'ultima osservazione degna di nota riguarda il linguaggio Objective-C analizzato.
Nonostante sia possibile sviluppare in Xcode con diversi linguaggi, Objective-C rimane il
principale soggetto. Tale linguaggio appare decisamente dinamico e semplice da utilizzare.
Bisogna comunque sottolineare che la documentazione distribuita è esclusivamente in
inglese, non prevede quindi alcun tipo di localizzazione e sopratutto non è reperibile
facilmente in forma cartacea. Inoltre è da notare l'enorme supporto che la Apple offre su
Objective-C agli sviluppatori. Tale documentazione non è però all'altezza della fama
Apple, in quanto le sezioni on line risultano ostiche e di difficile interpretazione.
Probabilmente meno materiale ma più curato sarebbe stato ottimale. Altre documentazioni
ottenute nelle ricerche in internet invece sono risultate di ottima qualità, alcune di queste
erano documenti distribuiti da Apple sotto forma di file Pdf. La documentazione offerta
comunque riguarda quasi esclusivamente framework, strumenti e linguaggio di sviluppo,
scarsa invece è quel che riguarda il sistema operativo nei sui strati più bassi.
Strumenti e linguaggi per lo sviluppo di applicazioni in ambiente iPhone OS
188
Bibliografia Alfieri, V. (2006). Implementazione di un servizio Voip in ambiente SOA per mobile computing. Apple. (s.d.). Introduction to Cocoa Fundamental Guide. Tratto da Developer Connection: http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals Apple. (s.d.). The Cocoa Environment. Tratto da Developer Connection: http://developer.apple.com/Cocoa/Conceptual/CocoaFundamentals Apple. (s.d.). The development Environment. Tratto da http://developer.apple.com/Cocoa/Conceptual/CocoaFundamentals Apple. (2006). The Objective-C 2.0 Programming Language. Ascione, P. (2005). Progetto ed implementazione di un Data Collector per Sistemi Operativi Symbian. Cesare, M. D. (2007). Il sistema Operativo Mac OS X.