MaCocoa 006

Capitolo 006 - Objective C

Continua la descrizione del linguaggio di programmazione Objective C.

Sorgenti: Segue da Objective C

Primo inserimento: 24 Ottobre 2001

Definizione di una classe

Una classe si definisce in due passi successivi: l'interfaccia e la realizzazione (interface e implementation); con l'interfaccia si dichiarano i metodi e le variabili d'istanza. Con la realizzazione si definisce (finalmente) la classe, scrivendo il codice che realizzai metodi.

Questa bipartizione si riflette nella (non necessaria, ma caldamente raccomandata) separazione della definizione di classe in due file separati; ancora, il nome dei file è libero, ma è caldamente raccomandato utilizzare il nome della classe con apposito suffisso.

Quindi, l'interfaccia della classe MiaClasse si scrive all'interno del file miaclasse.h, la realizzazione si scrive all'interno del file miaclasse.m (se non l'ho già detto, ignoro totalmente il perché del suffisso m ai file objC).

È ugualmente sconsigliato (ma non impedito) dichiarare/definire più di una classe per file.

Tutte queste prescrizioni servono per rendere poi più facile il lavoro del compilatore, ed anche del programmatore, che con queste poche e semplici regole sa sempre dove mettere le mani per lavorare senza troppa fatica. La divisione in due file del lavoro invece deriva dalla filosofia della OOP, che richiede una netta separazione tra l'interfaccia e la realizzazione.

Si possono utilizzare con profitto oggetti di cui si conosce la sola interfaccia (quindi, il solo file .h) e si ignora totalmente la struttura interna su cui si basa il funzionamento (il file .m).

La cosa avviene talmente spesso che poi non ci si fa più caso...

L'interfaccia

La dichiarazione di una classe funziona in questo modo: si dice che abbiamo un'interfaccia di una classe, e che questa classe è figlia di questa superclasse. Segue la dichiarazione delle variabili d'istanza, e poi dai metodi dell'oggetto. Fine. In linguaggio ObjC:

@interface NomeDellaClasse : NomeDellaSuperClasse
{
    dichiarazione delle variabili d'istanza
}
dichiarazione dei metodi
@end

È fondamentale mettere il nome della superclasse, altrimenti la nostra classe diverrebbe una classe root; in tal caso, sono tante e tali le variabili d'istanza ed i metodi da definire per rendere tale classe funzionante correttamente che non ne vale la pena. Quindi, mettiamo sempre il nome della superclasse. Se proprio vogliamo avere classi figlie di nessuno, mettiamo almeno NSObject.

Mirko Viviani mi corregge: il lavoro per realizzare una classe root non è necessariamente tantissimo. Rimane invece complicato realizzare una nuova classe root che faccia concorrenza e possa riampiazzare NSObject.

La dichiarazione della variabili d'istanza utilizza i tipi standard del C: possiamo quindi dichiarare variabili di tipo intero (int, con varie dimensioni: short, long), floating point (float, double), ed anche un po' di tipi aggiuntivi messi a disposizione (come altri oggetti...).

La definizione dei metodi richiede qualche commento. In primo luogo, occorre distinguere tra i metodi di classe (che si possono applicare agli oggetti Classe) e metodi applicabili a tutte le istanze (metodi d'istanza). Questi metodi si distinguono per il primo carattere della dichiarazione dei metodi: nel caso di metodi di classe, si comincia con il carattere '+', gli altri cominciano con '-'. Ad esempio il metodo principe per la creazione di oggetti, vale a dire alloc, si applica agli oggetti classe, quindi è dichiarato come:

+ alloc ;

(poiché NSObject definisce il metodo alloc ereditato da tutti gli oggetti, non occorre quasi mai ridefinire questo metodo).

Un metodo normale invece si dichiara in questo modo. Considero un metodo che non ha argomenti e non restituisce risultati:

- (void) mioMetodo ;

Vediamo invece come si dichiarano i tre metodi visti nel capitolo precedente per il movimento del mouse:

- (void) moveToX: (int)coordX moveToY: (int) coordY ;
- (void) moveToXY: (int)coordX : (int) coordY ;
- (void) moveToXY: (int)coordX, ... ;

Usare altre classi

Capita di dover utilizzare altri oggetti all'interno di un oggetto. Anzi, è certo: per definire una classe deve essere nota almeno la superclasse. Ecco che arriva la potenza della OOP: non è assolutamente importante come è realizzata questa superclasse; ci basta sapere qual è la sua interfaccia verso l'esterno. In altre parole, ci basta sapere come è dichiarata questa classe.

Da sempre, il C mette a disposizione un meccanismo per evitare di scrivere due volte le stesse cose (la dichiarazione della superclasse, nel caso), ovvero la direttiva (al precompilatore) #include. Una direttiva al precompilatore è semplicemente un comando, al di fuori del linguaggio C (o C++ o Objective C), diretto solo al precompilatore; quando il precompilatore incontra questo comando, esegue le particolari operazioni richieste (ad esempio, #include ricopia il file .h all'interno del file dove si trova questa direttiva). Un uso accorto delle direttive al precompilatore rende molto più facile programmare, leggere e documentare un programma scritto in C. Viceversa, un uso poco accorto delle direttive rende incomprensibile un programma, difficile programmare, impossibile da correggere.

Aneddoto: ricordo di essermi imbattuto anni fa in una specie di concorso di 'obfuscated C', in cui i partecipanti dovevano scrivere un programma in C, perfettamente funzionante e quindi corretto sintatticamente, ma che fosse il più incomprensibile e 'offuscato' alla comprensione possibile. Uno dei partecipanti appunto produsse un programma che, con l'uso spropositato delle direttive al precompilatore, consisteva in pratica in una sola lettera. Fine aneddoto (poco interessante, me ne rendo conto...).

Il succo del discorso è che utilizzare le direttive #include può diventare complicato quando il programma comincia a diventare più complesso di un semplice algoritmo di calcolo. È per questo che ho scoperto con entusiasmo una direttiva al compilatore, che funziona con Objective C (ed anche col C++, ho scoperto), che sostituisce comodamente la #include, ovvero la direttiva

#import    <nomefile.h>

che ha lo stesso effetto della direttiva #include, ma che in più evita di incorporare due volte lo stesso file. Mi spiego meglio: supponiamo di avere un file .m (o .c, non fa differenza) più o meno siffatto:

#include    "file1.h"

<<serie di istruzioni C>>

#include "file1.h"

<<ancora istruzioni C>>

Succede che il file file1.h è inserito due volte. Un compilatore mediamente intelligente produce una quantità di errori mostruosa in sede di compilazione. Utilizzando invece la direttiva #import, il precompilatore evita di inserire due volte il file file1.h, ed il compilatore è contento (in realtà esiste una tecnica molto semplice ma noiosa per ottenere lo stesso risultato senza la direttiva #import, ma visto che c'è questa direttiva, non ne parlo).

Mirko Viviani mi ricorda che il costrutto 'import' funziona teoricamente solo con i file Objective C e che non è necessariamente portabile su altre piattaforme.

C'è un'altra possibilità di utilizzare un'altra classe all'interno di una classe, che chiamerò certificato di esistenza in vita. La sintassi è molto più semplice di ogni spiegazione. All'interno del file che usa la classe si scrive

@class NomeClasseEsistente ;

Questa espressione (che temo essere una direttiva al precompilatore ObjC) informa semplicemente il compilatore (ed il linker e tutto il resto) ed il programmatore che da qualche parte esiste una classe che si chiama NomeClasseEsistente. Ora, è chiaro che non è che si possano fare molte cose con una Classe senza avere un'idea di come sia fatta (cioè, senza conoscere la sua interfaccia).

In effetti con questo metodo di certificazione si dichiara l'esistenza di classi che sono utilizzate solo come argomenti di metodi, come variabili d'istanza, eccetera. Se poi dobbiamo utilizzarle realmente, non vedo altre possibilità che importare il file .h corrispondente. Stabilisco quindi la seguente regola (operativa, cioè: finché funziona, la uso; appena vedo che non va, la abbandono): in un file .h che contiene la dichiarazione di una classe, importo sempre il file.h della superclasse, e cerco di utilizzare il più possibile la direttiva @class per utilizzare altre classi; in un file .m che contiene la definizione di una classe, importo i file .h delle classi che utilizzo.

I vantaggi dell'interfaccia

L'interfaccia di una classe, il file .h, è insomma tutto ciò che serve per lavorare con questa classe; infatti l'interfaccia:

È per questi motivi (e molti altri) che chi programma pulitamente in ObjC produce un file .h per ogni classe.

La realizzazione

La realizzazione di una classe è più o meno come l'interfaccia. Si comincia col dichiarare tutte le variabili d'istanza, per poi definire i vari metodi, in modo molto simile a quanto visto sopra:

@implementation NomeDellaClasse : NomeDellaSuperClasse
{
    dichiarazione delle variabili d'istanza
}
definizione dei metodi
@end

Ma, attenzione adesso ai giochi di prestigio:

Quindi, ciò che rimane è molto più semplice:

#import    "NomeDellaClasse.h"
@implementation NomeDellaClasse
definizione dei metodi
@end

La definizione dei metodi somiglia molto una serie di funzioni. Se prima avevamo qualcosa come:

- (void) moveToX: (int)coordX moveToY: (int) coordY ;

adesso bisogna definirlo (insomma, scriverlo):

- (void) moveToX: (int) coordX moveToY: (int) coordY
{
    /* serie di istruzioni in C e ObjC */
}

All'interno della definizione dei metodi, si possono utilizzare le variabili d'istanza così come sono, col loro preciso nome. Ad esempio, se abbiamo dichiarato in PrimaClasse.h

@interface PrimaClasse : NSObject
{
    short    contatore ;
}
- (void) aggiungi ;
@end

in Primaclasse.m possiamo scrivere:

#import    "PrimaClasse.h"
@implementation PrimaClasse
- (void) aggiungi
{
    contatore ++ ;
}
@end

A questo punto mi sono stufato. So come si definisce una classe, comincio un po' ad usarle... ma, un momento... non so nulla dell'ambiente di sviluppo...

Licenza Creative Commons
Eccetto dove diversamente specificato, i contenuti di questo sito sono rilasciati sotto Licenza Creative Commons.
Pagina a cura di Livio Sandel (macocoa2012@gmail.com).