DevTech #1 – Una panoramica sull’Object-C

Nei vari tutorial che ho scritto per “iSpazio” e per “The Bubi Devs” parecchi utenti lamentavano il fatto di una carenza di spiegazioni teoriche. Il linguaggio Objective-C, su cui si basa l’ambiente Cocoa, infatti, non è uno dei più semplici, ma non è nemmeno così complesso come appare a prima vista.
In questa lezione vedremo di spiegare le basi dell’Objective-C, che non è niente di nuovo, è un’estensione del famosissimo C.

Nota importante: Prima di iniziare questa lezione teorica, voglio specificare che questa non sarà una guida sui concetti fondamentali della programmazione e del linguaggio C, ma sarà una panoramica sugli aspetti più generali e più importanti al fine dello sviluppo di applicazioni per iPhone.
Ai più inesperti, quindi, consiglio prima di studiare le basi del linguaggio C. In rete di materiale se ne trova a volontà, bisogna solo attrezzarsi di buona volontà e studiare!

L’Objective C: un’estensione del C
Come si può facilmente capire dal nome, l’Objective-C è un’estensione del linguaggio C, di cui riprende la sintassi e le regole. A differenza del C++, l’Objective-C mantiene la completa compatibilità con il C, ovvero mantiene tutte le caratteristiche, aggiungendone di nuove.
Non voglio dilungarmi su aspetti storici, quindi rimando i più interessati a Wikipedia.
Come già avveniva nel linguaggio C, si definiscono le dichiarazioni e le implementazioni file separati. Le estensioni utilizzate dall’Objective-C sono le seguenti:

Estensione Tipo di codice
.h File headers (di intestazione). Contengono le dichiarazioni di classi, tipi, funzioni, costanti, etc.
.m File di implementazione. È l’estensione utilizzara per i file che contengono codice Objective-C e C, e che implementano determinate funzioni o classi.
.mm File di implementazione. Sono uguali ai file .m, ma inoltre contengono codice scritto in C++.

Ora ci viene un dubbio: come facciamo a collegare un file di intestazione ad uno di implementazione? La risposta è semplicissima: usiamo l’istruzione #import. Per chi avesse già conoscenza del C, si ricorda forse #include. Le due istruzioni sono praticamente uguali, con la sola differenza che #import fa in modo che lo stesso file non sia mai incluso più di una volta. Apple nella sua documentazione e nei suoi esempi preferisce usare #import, e anche noi faremo lo stesso.

Le stringhe
Chi di voi avrà familiarità con il C avrà quasi odiato le strighe, per la mancanza di funzioni ben strutturate (al contrario ad esempio di Java). Ecco perchè l’Objective-C utilizza istruzioni sue per la manipolazione di stringhe, nonostante supporti quelle tradizionali del C. Le convenzioni sono sempre le medesime, ovvero le stringhe sono racchiuse tra doppi apici (esempio: “ciao”), mentre i singoli caratteri tra apici (ovvero: ‘c’).

L’Objective-C fornisce un oggetto (un oggetto, non un tipo) per le stringhe: NSString. Questa classe fornisce moltissimi metodi per lavorare sulle stringhe, ad esempio metodi per separare una stringa, per pulirla da spazi, per codificarla tramite un determinato stardard, etc.

Prima ho sottolineato il fatto che la classe NSString sia un oggetto, e non un tipo. Questo significa che ogni istanza andrebbe inizializzata proprio come si fa con gli oggetti. L’Objective-C, però, ci da una mano, e ci permette di usare il simbolo @ per creare le stringhe in maniera veloce. Eccovi due esempi dimostrativi:

NSString* stringa1 = @”La mia bellissima stringa”;
NSString* stringa2 = [NSString stringWithFormat:@”%d %s”, 1, @”Una stringa meno bellissima”];

Il risultato finale sarà lo stesso, ovvero avremo due stringhe (seppur con contenuto diverso), però la prima istruzione è sicuramente più comoda e veloce da utilizzare.

Classi
Come si può dedurre dal nome stesso, l’Objective-C è un linguaggio orientato agli oggetti. È palese, quindi, che fornisca tutte quelle istruzioni che ci permetteranno di implementare e definire oggetti, classi e istanze.

La dichiarazione di una classe si compone di due parti distinte: l’interfaccia e l’implementazione. L’interfaccia contiene tutte le definizioni, quella della classe, delle variabili e dei metodi. L’implementazione, invece, fornisce il codice vero e proprio, che ha il compito di svolgere determinate funzioni. Vediamo ora questo esempio:

Nella prima riga leggiamo “@interface MiaClasse : NSObject”, questa è la dichiarazione della classe. “NSObject” è la classe parent, ovvero la superclassem quella da cui la nostra classe eredita i metodi principali. Tutte le classi che noi andremo ad istanziare ereditano come superclasse NSObject, ma potranno anche ereditarne altre. Questo è uno dei principi della programmazione ad oggetti.
Proseguendo con l’analisi del codice, troviamo delle variabili dichiarate tra due parentesi graffe. A seguire abbiamo poi la dichiarazione di due metodi: non preoccupiamoci di capire come siano strutturati, quello verrà analizzato dopo. Possiamo però notare che non c’è nessuna implemetazione, non scriviamo nessuna azione che i nostri metodi e la nostra classe dovrà eseguire. L’ultima istruzione è “@end”, che chiude la definizione della nostra classe.

Abbiamo visto, così, come dichiarare una classe. Vediamo come implementarla. Iniziamo con osservare l’implementazione dei due metodi che abbiamo dichiarato prima:

La nostra analisi inizia vedendo come ci sia analogia con la dichiarazione della classe che abbiamo visto in precedenza. Anche questo codice inizia con @implementation e finisce con @end. Queste due istruzioni, in sostanza, identificano l’inizio e la fine dell’implementazione della nostra classe. All’interno di queste due istruzioni troviamo il metodo “inizializza” setta dei valori di default, ovvero inizializza le variabili. Nelle variabili di tipo numerico (come “numero”) il valore è zero, mentre negli oggetti in generale tale valore corrisponderà a nil (ovvero il più generico null, che identifica un oggetto nullo, privo di contenuto).

Metodi
Una classe in Object-C può dichiarare due tipi di metodi: di istanza o di classe. Qual è la differenza? Un metodo di istanza di riferisce ad una particolare istanza della classe. Ovvero, per essere invocato prima deve essere creata un’istanziata della classe, deve essere creato un oggetto. Un metodo di classe, invece, non richiede nessuna istanza, e va ad operara su alcune proprietà generali della classe.

Vediamo un esempio di dichiarazione di un metodo, poi spiegheremo tutti i componenti:

Analizziamo la dichiarazione di questo metodo d’istanza. La signatura del metodo è “insecrisciOggetto: allaPosizione:”, ovvero il ‘nome’ del metodo. Il meno presente all’inizio è l’identificativo del tipo di metodo, ovvero in questo caso d’istanza. Quindi, se un metodo inizia con un meno (-) sarà un metodo d’istanza, mentre se compare un più (+) esso sarà un metodo di classe. Il (void) è il tipo di ritorno, ovvero il tipo di oggetto ritornato dal metodo, il tipo di ‘risultato’. In questo caso, ‘void’ significa un assenza di ritorno, ovvero questo nostro metodo eseguirà determinate azioni, ma non ritornerà nessun valore. In altri casi avremmo potuto avere (BOOL), (int), sarebbero stati diversi tipi di ritorno. La stessa considerazione vale per i tipi di parametro. I nomi dei parametri, invece, è il nome che assumono quei parametri all’interno del nostro metodo. Ovvero, nel nostro metodo “inserisciOggetto:” avremo una variabile “unOggetto”, che non sarà definita all’interno, ma sarà proprio il nosro parametro.

La struttura generale di un metodo sarà quindi:

tipo_metodo (tipo_di_ritorno) nome_del_metodo: (tipo_parametro) nome_parametro

Abbiamo visto come definire un metodo. Ora la domanda è: come faccio a richiamare il mio metodo? Anche in questo caso esiste una struttura precisa, che andiamo subito a vedere con un esempio:

[mioArray inserisciOggetto:unOggetto allaPosizione:5];

La prima cosa che notiamo è il fatto che la chiamata di un metodo è racchiusa tra due parentesi quadre [ ]. All’interno abbiamo poi il nostro metodo, e l’oggetto su cui vogliamo richiamarlo. A sinistra, infatti, vi è “mioArray”, che è l’oggetto su cui eseguiamo il metodo “inserisciOggetto”. La sintassi del metodo, invece, è del tutto uguale a quella precedente; dopo “inserisciOggetto” troviamo infatti “unOggetto”, che è l’oggetto che vogliamo inserire all’interno del nostro array; subito dopo abbiamo “allaPosizione”, che riceve il valore di 5, ovvero la posizione in cui dovremo inserire il nostro oggetto. Semplice no?

L’Objective-C ci consente di nidificare più chiamate. Ad esempio potremmo avere:

[ [ myAppObject getArray ] inserisciOggetto:[ myAppObject getObjectDaInserire ] allaPosizione:18 ];

Non preoccupiamoci di quello che esegue questo metodo, ma possiamo vedere come abbiamo effettuato più chiamate di metodo all’interno di una chiamata. Possiamo quindi nidificare le nostre chiamate.

Questi due esempi, però, si riferiscono a metodi che non ritornano nessun valore: e se il nostro metodo ci ritornasse un oggetto come risultato? Come ci dobbiamo comportare?
Osserviamo queste istruzioni, che hanno il compito di creare e inizializzare un array:

NSMutableArray* mioArray = nil;
mioArray = [NSMutableArray arrayWithCapacity:10 ];

Nella prima istruzione abbiamo definito il nostro array, e lo abbiamo inizializzato a null (nil). Nella seconda istruzione, invece, abbiamo chiamato il metodo “arrayWithCapacity” che ha creato un array con 10 elementi, ed è stato poi associato al nostro “mioArray”. Anche in questo caso niente di strano.

Protocolli e delegati
Un protocollo dichiara metodi che possono essere implementati da ogni classe. I protocolli, però, non sono classi. Essi definiscono semplicemente un’interfaccia, una “promessa”, che altri oggetti devono implementare. Quando un oggetto o una classe implementa i metodi di un protocollo, si dice che sono conformi a tale protocollo.
Nell’OS dell’iPhone, i protocolli sono usati molto frequentemente per implementare gli oggetti “delegate” (delegati). Un oggetto delegato è un oggetto che agisce in coordinamento o per conto di un altro oggetto.

La classe UIApplication implementa il comportamento richiesto di un’applicazione. Invece di dover fare una sottoclasse di UIApplication per ricevere semplici notifiche circa lo stato attuale dell’applicazione, la classe UIApplication fornisce tali notifiche chiamando metodi specifici di un determinato oggetto delegato. Un oggetto che implementa i metodi del protocollo UIApplicationDelegate può ricevere tali notifiche e fornire una risposta adeguata.
Eccovi come si dichiara un protocollo (molto simile ad una classe):

@protocol MioProtocollo
– (void) myProtocolMethod;
@end

Fonti:

Ingegnere informatico e sviluppatore freelance, mi occupo da anni di sviluppo per iOS (ma non solo). Dal 2008 scrivo su questo piccolo blog (con qualche lunga pausa), in cui parlo di programmazione e di qualsiasi altra cosa che mi diverta.

3 comments On DevTech #1 – Una panoramica sull’Object-C

  • ciao !!!
    Da non molto h cominciato a programmare in object-c Con “Leopard 10.5” xcode ho letto questa lezione e mi chiedevo se si possono richimare variabili esistenti ,e come posso farlo?
    mi spiego:

    -(double)CalculateDistance: PointA:(MapObject*)p_oPointA: PointB:(MapObject*)p_oPointB{
    //qui la mia variabile<——
    return 0;

    }

    -(double)CalculateAzimuth: PointA:(MapObject*)p_oPointA: PointB:(MapObject*)p_oPointB
    :Latitudine:Longitudine

    {
    ///qui richiamarla<————-
    return 0;
    }

    grazie per qualsiasi tipo di aiuto!!!!
    ciao.

  • @leonardo: ciao.. in che senso richiamare variabili esistenti?

    se hai dichiarato una variabile nel file .h sarà accessibile in tutta la classe..

Leave a reply:

Your email address will not be published.

Site Footer