sabato 10 aprile 2010

Guida Objective-C in Italiano - Le Classi


Ecco il secondo capitolo della Guida all'Objective-C, questo post sarà dedicato alle Classi e prosegue i concetti introduttivi dell'Objective-C discussi nell'articolo precedente.
Questo post è una traduzione in italiano della Guida all'Objective C, presente sul sito developer.apple.com a questo link. L'Objective C è il linguaggio di programmazione per creare applicazioni per iPhone. Questo articolo potrebbe interessare a tutti gli sviluppatori che vogliono imparare le basi di questo linguaggio che è il fondamento per programmare nuove applicazioni per Mac Os X, iPhone ed iPad.


Le Classi

Un programma OO è tipicamente costituito da una varietà di oggetti. Un programma basato sui frameworks Cocoa potrebbero usare oggetti NSMatrix, oggetti NSWindow, oggetti NSDictionary, oggetti NSFont, oggetti NSText, e molti altri. I programmi spesso usano più di un oggetto dello stesso tipo o classe - diversi oggetti NSArray o oggetti NSWindow, ad esempio.

In Objective-C, definisci oggetti definendo la loro classe. La definizione di classe è un prototipo per un tipo di oggetti; dichiara le variabili di istanza che diventano parte di ogni membro della classe, e definisce un insieme di metodi che tutti gli oggetti della classe possono usare.

Il compilatore crea solo un oggetto accessibile per ogni classe, un class object che conosce come costruire nuovi oggetti appartenenti alla classe. (Per questo motivo è tradizionalmente chiamato un "factory object"). La classe oggetto è la versione compilata della classe; gli oggetti che costruisce sono istanze della classe. Gli oggetti che svolgono il lavoro principale del tuo programma sono istanze create dall'oggetto classe a runtime.

Tutte le istanze di una classe hanno lo stesso insieme di metodi, e un insieme di variabili di istanza. Ogni oggetto ha le sue proprie variabili di istanza, ma i metodi sono condivisi.

Per convenzione, i nomi delle classi iniziano con lettera maiuscola (come Rectangle); i nomi delle istanze iniziano tipicamente con una lettera minuscola (come myRectangle).


Ereditarietà

Le definizioni di classi sono aggiuntive; ogni nuova classe che definisci è basata su un'altra classe dalla quale eredita metodi e variabili di istanza. La nuova classe semplicemente aggiunge o modifica ciò che eredita. Non necessita di duplicare il codice ereditato.

L'ereditarietà collega tutte le classi insieme in un albero gerarchico con una singola classe alla radice. Quando scrivi un codice che è basato sul Foundation framework, quella classe radice è tipicamente NSObject. Ogni classe (tranne la radice) ha una superclasse un passo più vicina alla radice, ed ogni classe (incluso la classe radice) può essere la superclasse di un qualsiasi numero di sottoclassi un passo più lontano dalla radice. la figura 1-1 illustra la gerarchia per alcune delle classi usate nel programma di disegno.


Figure 1-1 Alcune classi del programma di disegno



Questa figura mostra che la classe Square è una sottoclasse della classe Rectangle, che è una sottoclasse di Shape, Shape è una sottoclasse di Graphic, e Graphic è una sottoclasse di NSObject. l'ereditarietà è cumulativa. Quindi gli oggetti di tipo Square hanno i metodi e le variabili di istanza definite per le classi Rectangle, Shape, Graphic, e NSObject, oltre a quelle definite specificamente per Square. Questo per dire che un oggetto quadrato (Square), non è solo un quadrato, è anche un rettangolo, una forma, un grafico, e un NSObject.

Ogni classe tranne NSObject può quindi essere vista come una specializzazione o un'adattamento di un'altra classe. Ogni sottoclasse successiva modifica il totale di ciò che è ereditato cumulativamente. La classe Square definisce solo il minimo necessario per trasformare un Rettangolo in un Quadrato.
Quando definisci una classe, puoi collegarla alla gerarchia dichiarando la sua superclasse; ogni classe che crei deve essere la sottoclasse di un'altra classe (a meno che non stai definendo una nuova classe radice). C'è un'abbondanza di superclassi disponibile. Cocoa include la classe NSObject e molti frameworks che contengono definizioni per più di 250 classi aggiuntive. Alcune sono classi che puoi usare ingorporandole nel tuo programma. Altre potresti volerle adattare alle tue proprie esigenze definendo una sottoclasse.

Alcune classi dei framework definiscono quasi tutto ciò di cui puoi aver bisogno, ma lascia alcune specifiche da implementare in una sottoclasse. Puoi quindi creare oggetti molto sofisticati scrivendo poche linee di codice, e riutilizzando il lavoro effettuato dai programmatori del framework.


La classe NSObject

NSObject è una classe radice, e quindi non ha una superclaasse. Definisce il framework base per gli oggetti Objective-C e le interazioni tra gli oggetti. Trasmette alle classi e alle istanze delle classi che ereditano da essa l'abilità di comportarsi come oggetti e cooperare con il sistema di runtime.
Una classe che non ha bisogno di ereditare nessun comportamento speciale da un'altra classe dovrebbe essere fatta come sottoclasse della classe NSObject. Le istanze della classe devono avere almeno l'abilità di comportarsi come oggetti Objective-C a rutime. Ereditando quest'abilità dalla classe NSObject è più semplice e più affidabile che reinventare una nuova definizione di classe.

Nota: Implementare una nuova classe radice, è un'attività delicata e con molti azzardi nascosti. La classe deve duplicare molto di quello che NSObject fà, come allocare istanze, connetterle alla loro classe, e identificarle al sistema di runtime. Per questo motivo, dovreste usare generalmente la classe NSObject fornita con Cocoa come classe radice.


Ereditare variabili di istanza

Quando un oggetto classe crea una nuova istanza, il nuovo oggetto contiene non solo le variabili di istanza che erano definite per la sua classe, ma anche le variabili di istanza definite per la sua superclasse e per la superclasse della superclasse, e così via tornando indietro fino alla classe radice. Quindi, la variabile di istanza isa definita nella classe NSObject diventa parte di ogni oggetto. isa connette ogni oggetto alla sua classe.

La Figura 1-2 mostra alcune delle variabili di istanza che possono essere definite per una particolare implementazione di Rectangle, e da dove potrebbero venire. Nota che le variabili che fanno dell'oggetto un Rectangle sono aggiunte a quelle che lo rendono una forma (classe Shape), un grafico (classe Graphic) e così via.


Figure 1-2 Variabili di istanza di Rectangle

Una classe non deve dichiarare variabili di istanza. deve semplicemente definire nuovi metodi e contare sulle variabili di istanza che eredita, se ha bisogno di qualsiasi variabile di istanza affatto. Per esempio, Sqare potrebbe non dichiarare nessuna variabile di istanza propriamente sua.


Ereditare metodi

Un oggetto ha accesso non solo ai metodi definiti per la sua classe, ma anche a quelli della sua superclasse e per la superclasse della superclasse, e così via tornando indietro fino alla classe radice della gerarchia. Un oggetto Square può usare metodi definiti in Rectangle, Shape, Graphic e NSObject così come quelli definiti nella sua propria classe.

Ogni nuova classe che definisci in un programma può quindi fare uso di codice scritto per tutte le classi sopra di lei nella gerarchia. Questo tipo di eredità è il maggior vantaggio della programmazione OO. Quando usi uno dei frameworks OO forniti da Cocoa, i tuoi programmi possono avvantaggiarsi delle funzionalità codificate nelle classi del framework. Devi solo aggiungere il codice che personalizza le funzionalità standard della tua applicazione.

Anche gli oggetti classe ereditano dalla loro superclasse. Ma poichè non hanno variabili di istanza, ereditano soltanto i metodi.


Sovrascrivere un metodo con un altro

C'è un'utile eccezione all'ereditarietà: quando definisci una nuova classe, puoi implementare un nuovo metodo con lo stesso nome di un metodo definito in una delle classi più in alto nella gerarchia. Il nuovo metodo sovrascrive l'originale; le istanze della nuova classe eseguono il nuovo piuttosto che l'originale, e sottoclassi della nuova classe lo ereditano piuttosto di ereditare l'originale.Ad esempio, Graphic definisce un metodo dispolay che Rectangle sovrascrivere per definire la sua propria versione di dispolay. il metodo di Graphic è disponibile a tutti gli oggetti che ereditano dalla classe graphic, ma non agli oggetti Rectangle, che invece eseguono la loro propria versione di display.
Sebbene sovrascrivere un metodo blocchi la versione originale dall'essere ereditata, gli altri metodi definiti nella nuova classe possono saltare il metodo ridefinito e trovare l'originale.
Un metodo ridefinito può anche incorporare il metodo che sovrascrive. Quando lo fà, il nuovo metodo serve solo per rifinire o modificare il metodo che sovrascrive piuttosto che sostituirlo del tutto. Quando diverse classi nella gerarchia definiscono lo stesso metodo, ma ogni nuova versione incorpora la versione che sovrascrive, l'implementazione del metodo è effettivamente sparsa tra tutte le classi.
Sebbene una sottoclasse può sovrascrivere i metodi ereditati, non può sovrascrivere le variabili di istanza. Dato che un oggetto ha memoria allocata per ogni variabile che eredita, non puoi sovrascrivere una variabile dichiarandone una nuova con lo stesso nome. Se provi, il compilatore protesterà.


Classi Astratte

Alcune classi sono progettate solo o soprattutto in modo che le altre classi possano ereditare da loro. Queste classi Astratte, raggruppano metodi e variabili di istanza che possono essere usate da un numero di diverse sottoclassi in una definizione comune. La classe astratta è tipicamente incompleta da sola, ma contiene codice utile a ridurre il carico di sue sottoclassi. (Poichè le classi astratte devono avere sottoclassi per essere utili, sono a volte chiamate superclassi astratte).
Diversamente da altri linguaggi, Objective-C non ha sintassi per marcare una classe come astratta, non previene da creare un'istanza di una classe astratta.
La classe NSObject è l'esempio canonico di una classe astratta in Cocoa. Non usi mai istanze di NSObject in un'applicazione - non sarebbe buona a niente; sarebbe un oggetto generico senza l'abilità di fare niente in particolare. La classe NSView, d'altra parte, fornisce un esempio di istanze di una classe astratta che potreste occasionalmente usare direttamente.

Le classi astratte contengono spesso codice che aiuta a definire la struttura di un'applicazione. Quando crei sottoclassi di queste classi, istanze delle tue nuove classi si adattano senza sforzo alla struttura dell'applicazione e funzionano automaticamente con gli altri oggetti.


Tipi di Classe

Una definizione di classe è una specifica per un tipo di oggetto. La classe in effetti definisce un tipo di dato. Il tipo è basato non solo sulla struttura della classe (variabili di istanza), ma anche sul comportamento(metodi).
Un nome di classe può apparire nel codice sorgente ogni volta che uno specificatore di tipo è permesso in C - ad esempio, come un argomento dell'operatore sizeof:


int i = sizeof(Rectangle);

Static Typing

Puoi usare un nome classe al posto di id per designare un tipo di oggetto:


Rectangle *myRectangle;
Poichè questo modo di dichiarare un tipo di oggetto dà al compilatore informazioni sul tipo di oggetto di cui si tratta, è conosciuto come static typing. Proprio come id è in effetti un puntatore, gli oggetti sono staticamente tipati come puntatori a una classe. Gli oggetti sono sempre di tipo puntatore. Lo Static Typing rende il puntatore esplicito; id lo nasconde.
Static typing permette al compilatore di effettuare qualche controllo di tipo - ad esempio, di avvisare se un oggetto può ricevere un messaggio a cui sembra non essere ingrado di rispondere - e per allentare alcune restrizioni che applicano agli oggetti tipati genericamente id. In più, può chiarire le tue intenzioni agli altri che reggono il tuo codice sorgente. Comunque, questa tecnica non batte il dynamic binding o altera la determinazione dinamica di una classe del ricevente a runtime.
Un oggetto può essere tipato staticamente alla sua propria classe o a qualunque altra classe da cui eredita. Ad esempio, dato che l'ereditarietà fa di un Rettangolo un tipo di Grafico, un'istanza di un Rectangle potrebbe essere staticamente tipata alla classe Graphic:


Graphic *myRectangle;

Introspezione dei tipi

Le istanze possono rivelare i loro tipi a runtime. Il metodo isMemberOfClass: definito nella classe NSObject, controlla se il ricevente è un'istanza di una classe particolare:


if ( [anObject isMemberOfClass:someClass] )

...

Il metodo isKindOfClass:, definito anche nella classe NSObject, controlla più generalmente se il ricevente eredita da o è un membro di una classe particolare (se ha la classe nel suo cammino gerarchico):


if ( [anObject isKindOfClass:someClass] )

...

L'insieme di classi per le quali isKindOfClass: restituisce YES è lo stesso insieme al quale il ricevente può essere staticamente tipato.
L'Introspezione non è limitata a informazioni di tipo. Vedremo successivamente metodi che restituiscono oggetti classe, riportano se un oggetto risponde a un messaggio, e rivelano altre informazioni.


Class Objects

Una definizione di classe contiene vari tipi di informazione, molte delle quali riguardano le istanze della classe:


  • Il nome della classe e della sua superclasse
  • Un template che descrive un insieme di variabili di istanza
  • Le dichiarazioni dei nomi dei metodi e i loro tipi di ritorno e argomenti
  • Le implementazioni dei metodi
Questa informazione è compilata e registrata in strutture dati rese disponibili al sistema di runtime. il compilatore crea solo un grande oggetto, un oggetto classe, per rappresentare la classe. Il class object ha accesso a tutte le informazioni sulla classe, il che vuol dire alle principali informazioni su come sono le istanze della classe. Può produrre nuove istanze in accordo al piano presente nella definizione della classe. Sebbene un oggetto classe abbia il prototipo di un istanza di una classe, non è un'istanza di per sè. Non ha variabili di istanza proprie nè può eseguire metodi intesi specificatamente per la classe - i metodi classe sono l'opposto dei metodi istanza. Un oggetto classe eredita i metodi classe dalle classi sopra di lui nella gerarchia, proprio come le istanze ereditano i metodi istanza.
Nel codice sorgente, l'oggetto classe è rappresentato dal nome della classe. Nel seguente esempio, la classe Rectangle restituisce il numero di versione della classe usando un metodo ereditato da NSobject:


int versionNumber = [Rectangle version];

Comunque, il nome classe è supportato per la classe oggetto solo come un ricevente in un espressione di messaggi. Alltrove, avrete bisogno di chiedere a un'istanza o alla classe per restituire l'id di classe. Entrambi rispondono a un messaggio classe:


id aClass = [anObject class];

id rectClass = [Rectangle class];

Come mostrano questi esempi, gli oggetti classe possono, come gli altri oggetti essere tipati come id. Ma gli oggetti classe possono essere anche tipati più specificamente al tipo di dato Class:


Class aClass = [anObject class];

Class rectClass = [Rectangle class];

Tutti gli oggetti classe sono tipi Class. Usando questo seplice nome per una classe è equivalente ad usare il nome della classe per tipare staticamente un'istanza.
Gli oggetti classe sono quindi oggetti full-fledged che possono essere tipati dinamicamente, ricevere messaggi, ed ereditare metodi da altre classi. Sono speciali solo in quanto sono creati dal compilatore, mancano le strutture dati (variabili di istanza) tranne quelli costruiti nella definizione di classe, e sono gli agenti che producono istanze a runtime.
Nota: Il compilatore costruisce anche un oggetto metaclasse per ogni classe. Esso descrive l'oggetto classe proprio come la classe descrive le istanze della classe. Ma mentre puoi mandare messaggi a istanze e al class object, l'oggetto metaclasse è usato solo internamente dal sistema di runtime.


Creare Istanze

Una funzione principale di un oggetto classe è di creare nuove istanze. Questo codice dice alla classe Rectangle di creare una nuova istanza di Rectangle ed assignarla alla variabile myRectangle:


id myRectangle;

myRectangle = [Rectangle alloc];

Il metodo alloc alloca diamicamente la memoria per le variabili della nuova istanza dell'oggetto e le inizializza tutte a 0 - tutte, tranne la variabile isa che connette la nuova istanza alla sua classe. A un oggetto per esere utile, generalmente serve essere inizializzato più completamente. Questa è la funzione del metodo init. L'inizializzazione tipicamente segue subito dopo l'allocazione:


myRectangle = [[Rectangle alloc] init];

Questa linea di codice, o una come questa, potrebbe essere necessaria prima che myRectangle possa ricevere qualunque dei messaggi illustrati negli esempi precedenti. Il metodo alloc restituisce una nuova istanza e l'istanza esegue un metodo init per settare il suo stato iniziale. Ogni oggetto classe ha almeno un metodo (come alloc) che lo abilita a produrre nuovi oggetti, ed ogni istanza ha almeno un metodo (come init) che la prepara all'uso. I metodi di inizializzazione prendono spesso argomenti per permettere di passare particolari valorie hanno parolechiave per etichettare gli argomenti (initWithPosition:size:, ad esempio, è un metodo che potrebbe inizializzare una nuova istanza di un Rectangle), ma inizia tutto con init.


Personalizzazione con Class Objects

Non è solo un capriccio del linguaggio Objective-C che le classi sono trattate come oggetti. è una scelta voluta, e a volte sorprendenti vantaggi per la progettazione. è possibile, ad esempio, personalizzare un oggetto con una classe, dove la classe appartiene a un insieme open-ended. Nell'Application Kit, ad esempio un oggetto NSMatrix può essere personalizzato in un particolare tipo di oggetto NSCell.
Un oggetto NSMatrix può prendersi la responsabilità di creare oggetti individuali che rappresentano le sue celle. Può farlo quando la matrice è prima inizializzata e dopo quando servono nuove celle. La matrice visibile che un oggetto NSMatrix disegna sullo schermo, può crescere e accorciarsi a runtime, forse in risposta alle azioni dell'utente. Quando cresce, la matrice necessita di poter produrre nuovi oggetti per riempire i nuovi slots che sono stati aggiunti.
Ma che tipo di oggetti dovrebbero essere? Ogni matrice mostra solo un tipo di NSCell, ma ce ne sono molti tipi diversi. La gerarchia ederitaria in figura 1-3 mostra alcune di quelle fornite dall'Application Kit. Tutte ereditano dalla classe generica NSCell:
Figure 1-3 Gerarchia ereditaria per NSCell

Quando una matrice crea oggetti NSCell, dovrebbero essere oggetti NSButtonCell per mostrare un insieme di bottoni o pulsanti, oggetti NSTextFieldCell per mostrare campi dove l'utente può inserire e modificare testo, o qualche altro tipo di NSCell? l'oggetto NSMatrix deve tenere conto di ogni tipo di cella, anche quelle non ancora inventate.
Una soluzione a questo problema è definire la classe NSMatrix come classe astratta e richiedendo ad ognuno che la vuole usare, di dichiararne una sottoclasse e implementare i metodi che producono nuove celle. Poiché stavano realizzando i metodi, gli utenti della classe dovrebbero accertarsi che gli oggetti che hanno creato siano del giusto tipo.
Ma questo richiede che altri svolgano il lavoro che dovrebbe essere fatto nella classe NSMatrix, e non necessariamente prolifera il numero di classi. Dato che un'applicazione può servire più di un tipo di NSMatrix, ognuna con un tipo diverso di NSCell, potrebbe diventare stipato con le sottoclassi di NSMatrix. Ogni volta che inventi un nuovo tipo di NSCell, dovrai anche definire un nuovo tipo di NSMatrix. Inoltre, programmatori su differenti progetti potrebbero virtualmente scrivere codice identico per fare la stessa cosa, tutto per compensare il fatto che NSMatrix non può farlo.
Una soluzione migliore, la soluzione che è attualmente adottata dalla classe NSMatrix, è di tenere conto che le istanze di NSMatrix siano inizializzate con un tipo di NSCell - con un oggetto classe. Definisce un metodo setCellClass: che passa l'oggetto classe per il genere di oggetto NSCell un NSMatrix dovrebbe usare per riempire spazi vuoti:


[myMatrix setCellClass:[NSButtonCell class]];
L'oggetto NSMatrix usa l'oggetto classe per produrre nuove celle, quando è inizializzato e ogni volta che è ridimensionato per contenere più celle. Questo tipo di personalizzazione sarebbe difficile se le classi non fossero oggetti che possono essere passati in messaggi e assegnati a variabili.


Variabili e Class Objects

Quando definisci una nuova classe, puoi specificare variabili di istanza. Ogni istanza della classe può mantenere la sua propria copia delle variabili che dichiari - ogni oggetto controlla i suoi propri dati. Comunque non ci sono "variabili di classe" come controparti alle variabili di istanza. Solo le strutture dati interne, inizializzate dalla definizione della classe, sono fornite per la classe. Inoltre, un class object non ha accesso alle variabili di istanza di qualsiasi istanza; non può inizializzarle, leggerle o modificarle.
Per tutte le istanze di una classe, per condividere i dati, bisogna definire una variabile esterna di un certo ordinamento. Il modo più sempice per farlo è dichiarare una variabile nel file di implementazione della classe come illustrato nel seguente frammento di codice:


int MCLSGlobalVariable;



@implementation MyClass

// implementation continues

In un'implementazione più sofisticata, puoi dichiarare una variabile statica, e fornire metodi di classe per gestirla. Dichiarare una variabile statica (static) limita il suo scope alla classe - e solo alla parte della classe che è implementata nel file. (Quindi a differenza delle variabili di istanza, le variabili statiche non possono essere ereditate da, o direttamente manipolate da sottoclassi). Questo pattern è comunemente usato per definire istanze di una classe condivise.


static MyClass *MCLSSharedInstance;



@implementation MyClass



+ (MyClass *)sharedInstance

{

// check for existence of shared instance

// create if necessary

return MCLSSharedInstance;

}

// implementation continues

Le variabili statiche aiutano a dare al class object più funzionalità; può avvicinarsi ad essere un oggetto più completo e versatile. Un class object può essere usato per coordinare le istanze che crea, dispensa istanze dalle liste di oggetti già creati, o gestisce altri processi essenziali all'applicazione. Nel caso che hai bisogno di un solo oggetto di una particolare classe, puoi mettere tutto lo stato dell'oggetto in una variabile statica e usare solo metodi classe. Questo salva i passi di allocare e inizializzare un'istanza.
Nota: è anche possibile usare variabili esterne che non sono dichiarate statiche, ma lo scope limitato di variabili statiche serve meglio allo scopo di incapsulamento dei dati in oggetti separati.


Inizializzare un class Object

Se vuoi usare un class object per qualunque cosa oltre allocare istanze, ti potrebbe servire inizializzarle come faresti con un istanza. Sebbene i programmi non allocano class objects, Objective-C fornisce un modo ai programmi per inizializzarli.
Se una classe fa uso di variabili statiche o globali, il metodi initialize è un buon posto per impostare i loro valori iniziali. Per esempio, se una classe mantiene un array di istanze, il metodo initialize potrebbe impostare l'array e anche allocare una o due istanze di default per averle pronte.
Il sistema di runtime invia un messaggio initialize ad ogni oggetto di classe prima che la classe riceva qualunque altro messaggio e dopo che la sua superclasse ha ricevuto il messaggio initialize. Questo da alla classe una possibilità di impostare ambiente di runtime prima che venga usata. Se non è richiesta nessuna inizializzazione, non avete bisogno di scrivere un metodo initialize per rispondere al messaggio.
Per l'ereditarietà, un messaggio initialize inviato ad una classe che non implementa il rispettivo metodo è inoltrato alla superclasse, anche se la superclasse ha già ricevuto il messaaggio initialize. Per esempio, assumiamo che la classe A implementi il metodo initialize, e la classe B eredita dalla classe A ma non implementa il metodo initialize. Appena prima che la classe B riceva il suo primo messaggio, il sistema di runtime gli invia initialize. Ma, poichè B non implementa initialize, al suo posto è eseguito il metodo initialize della classe A. Quindi, la classe A dovrebbe assicurare che la sua inizializzazione logica sia eseguita solo una volta, e per la classe appropriata. Per evitare l'inizializzazione logica pià di una volta usa il template nel listato seguente, quando implementa il metodo initialize.
Listing 1-3 Implementazione di un metodo initialize


+ (void)initialize

{

if (self == [ThisClass class]) {

// Perform initialization here.

...

}

}

Nota: Ricorda che il sistema di runtime invia initialize ad ogni classe individualmente. Quindi, in un'implementazione di classe del metodo initialize, non dovete inviare il messaggio initialize alla superclasse.


Metodi della Classe Radice

Tutti gli oggetti, classi e istanze, necessitano ugualmente un'interfaccia al sistema di runtime. Entrambi oggetti classe e istanze dovrebbero poter fare introspezione sulle loro abilità e riportare la loro posizione nell'ereditarietà gerarchica. è la provincia di NSObject a fornire quest'interfaccia.
Quindi questi metodi di NSObject non devono essere implementati due volte - una per fornire un'interfaccia di runtime per le istanze e ancora per duplicare quell'interfaccia per gli oggetti classe - ai class objects è data una dispensa speciale per eseguire metodi istanza definiti nella classe radice. Quando un class object riceve un messaggio, che non può rispondere con un metodo di classe, il sistema di runtime determina se c'è un metodo radice istanza che può rispondere. i soli metodi istanza che un class object può eseguire sono definiti nella classe radice, e solo se non c'è un metodo di classe che può fare il lavoro.


Nomi di classi nel codice sorgente

Nel codice sorgente, i nomi di classi possono essere usati solo in due contesti molto diversi. Questi contesti riflettono il ruolo duale di una classe come tipo di dati e come un oggetto:
Il nome classe può essere usato come il nome di un tipo per un genere di oggetto. Per esempio:


Rectangle *anObject;

Qui anObject è staticamente tipato come puntatore a un Rectangle. Il compilatore si aspetta che esso abbia la struttura dati di un'istanza di Rectangle e i metodi istanza definiti ed ereditati dalla classe Rectangle. Lo static typing abilita il compilatore ad eseguire migliori controlli di tipo e crea più codice sorgente e più auto documentazione. Solo le istanze possono essere staticamente tipate; gli oggetti classe non possono essere, dato che non sono membri di una classe, ma piuttosto appartenere al tipo di dato Class.
Come il ricevitore in un' espressione di un messaggio, i nomi di classi si riferiscono al class object. Quest'uso è stato illustrato in molti degli esempi precedenti. Il nome di classe può stare per l'oggetto classe solo come un messaggio ricevente.
Come il ricevente in un espressione di messaggi, il nome classe si riferisce al class object. Quest'uso è stato illustrato diverse volte negli esempi precedenti. I nomi di classe stanno per l'oggetto classe solo come un messaggio ricevente. In qualunque altro contesto, devi chiedere al class object di rivelare il suo id. L'esempio sotto passa la classe Rectangle come argomento di un messaggio isKindOfClass:.


if ( [anObject isKindOfClass:[Rectangle class]] )

...

Sarebbe stato illegale semplicemente usare il nome Rectangle come argomento. Il nome della classe può essere solo un ricevente. Se non sai il nome della classe al tempo di compilazione ma ce l'hai in una stringa a runtime, puoi usare NSClassFromString per restituire il class object::


NSString *className;

...

if ( [anObject isKindOfClass:NSClassFromString(className)] )

...

Questa funzione restituisce nil se la stringa passata non è un nome di classe valido. I nomi di classi esistono nello stesso namespace come le variabili globali e i nomi di funzioni. Una classe e una variabile globale non possono avere lo stesso nome. I nomi delle classi riguardano i soli nomi con visibilità globale.


Testare l'eguaglianza di Classi

Puoi testare due oggetti di classe per l'eguaglianza usando un confronto diretto. Ci sono molte caratteristiche nel framework Cocoa che dinamicamente e trasparentemente sottoclassano classi esistenti per estenderne le funzionalità. Quando succede, il metodo di classe è tipicamente sovrascritto, la sottoclasse dinamica è mascherata dalla classe rimpiazza. Quando testi per l'eguaglianza delle classi, puoi quindi confrontare i valori restituiti dal metodo classe piuttosto che quelli restituiti da un più basso livello di funzioni. Messo in termini di API:


[object class] != object_getClass(object) != *((Class*)object)
Puoi testare per l'eguaglianza due classi come segue:


if ([objectA class] == [objectB class]) { //...

Fine seconda parte

Finisce qui il secondo articolo dedicato alle basi del linguaggio di programmazione Objective C. Spero di realizzare un'utile manuale disponibile a tutti. Il prossimo post di questa guida, tratterà la definizione delle Classi, le Interfacce, l'implementazione di Classi e lo Scope delle variabili di istanza. Segnalatemi eventuali errori, o commentate l'articolo se l'avete trovato utile, in modo da incentivarmi a proseguire con le traduzioni.
Guida Obj-C parte 1

Nessun commento:

Posta un commento

Related Posts with Thumbnails