Visualizzazione post con etichetta tutorial. Mostra tutti i post
Visualizzazione post con etichetta tutorial. Mostra tutti i post

domenica 16 maggio 2010

Guida Objective-C in Italiano - C++ e Objective-C - Parte 15


Eccoci finalmente all'ultima parte della Guida all'Objective-C. L'argomento trattato oggi è l'uso di C++ e Objective-C.

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. Questa guida 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.


Usare C++ Con Objective-C

Il compilatore Objective-C di Apple ti permette di mixare liberamente C++ e Objective-C nello stesso file sorgente. Questo linguaggio ibrido Objective-C/C++ è chiamato Objective-C++. Con esso puoi fare uso di librerie C++ esistenti dalle tue applicazioni Objective-C.

Mixare Caratteristiche di Linguaggio Objective-C e C++

In Objective-C++, puoi chiamare metodi da l'uno o l'altro linguaggio, nel codice C++ e nei metodi Objective-C. I puntatori ad oggetti in uno o l'altro linguaggio sono solo puntatori, e come tali possono essere usati ovunque. Ad esempio, puoi includere puntatori a oggetti Objective-C come membri dati di classi C++, e puoi includere puntatori ad oggetti C++ come variabili di istanza di classi Objective-C.
Il listato 14-1 illustra questo.

Nota: Xcode richiede che i nomi dei files abbiano un'estensione ".mm" per le estensioni Objective-C++ per poter essere abilitate dal compilatore.

Listato 14-1 Usare C++ ed istanze Objective-C come variabili di istanza

/* Hello.mm

* Compila con: g++ -x objective-c++ -framework Foundation Hello.mm -o hello

*/



#import

class Hello {

private:

id greeting_text; // Contiene una NSString

public:

Hello() {

greeting_text = @"Hello, world!";

}

Hello(const char* initial_greeting_text) {

greeting_text = [[NSString alloc] initWithUTF8String:initial_greeting_text];

}

void say_hello() {

printf("%s\n", [greeting_text UTF8String]);

}

};



@interface Greeting : NSObject {

@private

Hello *hello;

}

- (id)init;

- (void)dealloc;

- (void)sayGreeting;

- (void)sayGreeting:(Hello*)greeting;

@end



@implementation Greeting

- (id)init {

if (self = [super init]) {

hello = new Hello();

}

return self;

}

- (void)dealloc {

delete hello;

[super dealloc];

}

- (void)sayGreeting {

hello->say_hello();

}

- (void)sayGreeting:(Hello*)greeting {

greeting->say_hello();

}

@end



int main() {

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];



Greeting *greeting = [[Greeting alloc] init];

[greeting sayGreeting]; // > Hello, world!



Hello *hello = new Hello("Bonjour, monde!");

[greeting sayGreeting:hello]; // > Bonjour, monde!



delete hello;

[greeting release];

[pool release];

return 0;

}

Come puoi dichiarare strutture C in interfacce Objective-C, puoi anche dichiarare classi C++ in interfacce Objective-C. Come con le strutture C, le classi C++ definite dentro un'interfaccia Objective-C hanno uno scope globale, non annidato entro la classe Objective-C. (ciò è coerente con il modo in cui standard C (sebbene non C++) promuova le definizioni di strutture nidificate allo scope del file).

Per permettervi di condizionare il vostro codice basato su una variante del linguaggio, il compilatore Objective-C++ definisce entrambe le costanti di preprocessore __clusplus e __OBJC__, come specificato dagli standards dei linguaggi C++ e Objective-C rispettivamente.

Come detto precedentemente, Objective-C++ non permette di ereditare classi C++ dagli oggetti Objective-C, nè permette di ereditare classi Objective-C da oggetti C++.
class Base { /* ... */ };

@interface ObjCClass: Base ... @end // ERROR!

class Derived: public ObjCClass ... // ERROR!

Diversamente dall'Objective-C, gli oggetti in C++ sono tipati staticamente, con polimorfismo a runtime disponibile come caso eccezionale. I modelli oggetto dei due linguaggi sono quindi non direttamente compatibili. Più fondamentalmente, il layout degli oggetti Obj-C e C++ in memoria è mutuamente incompatibile, ciò significa che generalmente è impossibile creare un'istanza di un oggetto che sarebbe valida dalla prospettiva di entrambi i linguaggi. Quindi, i due tipi di gerarchia non possono essere intercambiati.

Puoi dichiarare una classe C++ dentro una dichiarazione di calsse Objective-C. Il compilatore tratta queste classi come se fossero state dichiarate nel namespace globale, come segue:
@interface Foo {

class Bar { ... } // OK

}

@end



Bar *barPtr; // OK

Objective-C permette di usare strutture C (sia se dichiarate dentro una dichiarazione Objective-C sia se non dichiarate) come variabili di istanza.
@interface Foo {

struct CStruct { ... };

struct CStruct bigIvar; // OK

} ... @end

Su Mac OS X 10.4 e successivi, se imposti il flag del compilatore fobjc-call-cxx-cdtors, puoi usare istanze di classi C++ che contengono funzioni virtuali e costruttori non banali e decostruttori come variabili di istanza.( il flag fobjc-call-cxx-cdtors è impostato di default in gcc-4.2.) I Costruttori sono invocati nel metodo alloc (specificamente, dentro class_createInstance), in ordine di dichiarazione immediatamente dopo che l'oggetto di cui sono membri è allocato. Il costruttore usato è il "costruttore pubblico senza argomenti". I distruttori sono invocati nl metodo dealloc (specificamente, dentro object_dispose), in ordine inverso della dichiarazione immediatamente prima che l'oggetto di cui sono membri è deallocato.

Mac OS X v10.3 e precedenti: Le seguenti precauzioni si applicano solo a Mac OS X v10.3 e precedenti.

Objective-C++ in modo simile si sforza di permettere a istanze di classi C++ di servire come variabili di istanza. Questo è possibile fintanto che la classe C+ in questione non ha funzioni membro virtuali definite. Se qualunque funzione membro virtuale fosse presente, la classe C++ potrebbe non servire come una variabile d'istanza Objective-C.
#import



struct Class0 { void foo(); };

struct Class1 { virtual void foo(); };

struct Class2 { Class2(int i, int j); };



@interface Foo : NSObject {

Class0 class0; // OK

Class1 class1; // ERROR!

Class1 *ptr; // OK—call 'ptr = new Class1()' from Foo's init,

// 'delete ptr' from Foo's dealloc

Class2 class2; // WARNING - constructor not called!

...

@end

C++ richiede che ogni istanza di una classe che contiene funzioni virtuali contenga un puntatore adatto nella tavola delle funzioni virtuali. Comunque, il runtime dell'Objective-C non può inizializzare il puntatore della tavola delle funzioni virtuali, perchè non è familiare con il modello degli oggetti C++. In modo analogo, il runtime di Objective-C non può spedire chiamate a costruttori C++ o decostruttori per quegli oggetti. Se una classe C++ ha qualsiasi costruttore definito da utente o distruttore, essi non sono chiamati. Il compilatore emette un warning in tali casi.

Objective-C non ha una nozione di nasmespaces annidati. Non puoi dichiarare classi Objective-C dentro namespaces C++, nè puoi dichiarare namespaces dentro classi Objective-C.

Classi Objective-C, protocolli, e categorie non possono essere dichiarati dentro un template C++, nè un template C++ può essere dichiarato nello scope di un'interfaccia, protocollo o categoria Objective-C.

Comunque, classi Objective-C potrebbero servire come parametri di template C++. Parametri dei template di C++ possono anche essere usati come riceventi o parametri (comunque non come selettori) in espressioni di messaggi Objective-C.

C++ Ambiguità Lessicali e Conflitti

Ci sono pochi identificatori che sono definiti nei files header Objective-C che ogni programma Objective-C deve includere. Questi identificatori sono id, Class, SEL, IMP, e BOOL.
In un metodo Objective-C, Il compilatore pre-dichiara gli identificatori self e super, in modo simile alla parola chiave this in C++. Comunque, diversamente dalla parola chiave this in C++, self e super sono context-sensitive (sensibili al contesto); potrebbero essere usati come identificatori ordinari fuori ai metodi Objective-C.

Nella lista dei parametri dei metodi dentro un protocollo, ci sono altre cinque parole chiave context-sensitive (oneway, in, out, inout, and bycopy). Queste non sono parole chiavi in altri contesti.

Dal punto di vista di un programmatore Objective-C, C++ aggiunge parecchie nuove parole chiavi. Puoi usare ancora le parole chiavi C++ come parte di un selettore Objective-C, così l'impatto non è troppo severo, ma non puoi usarle per nominare classi Objective-C o variabili di istanza. Ad esempio, anche se class è una parola chiave C++, puoi ancora usare il metodo class: di NSObject.
[foo class]; // OK
Comunque, dato che è una parola chiave, non puoi usare class come il nome di una variabile:
NSObject *class; // Error
In Objective-C, i nomi per le classi e le categorie vivono in namespaces diversi. Che è, entrambi @interface foo e @interface(foo) possono esistere nello stesso codice sorgente. In Objective-C++, puoi anche avere una categoria i cui nomi combaciano con una classe o struttura C++.
Gli specificatori di protocollo e template usano la stessa sintassi per scopi diversi:
id foo;

TemplateType bar;

Per evitare quest'ambiguità, il compilatore non permette che id venga usato come nome di template.

Infine, c'è un'ambiguità lessicale in C++ quando un'etichetta è seguita da un'espressione che menziona un nome globale, come in:
label: ::global_name = 3;
Lo spazio dopo i primi due punti è richiesto. Objective-C++ aggiunge un caso simile, il quale richiede anch'esso uno spazio:
receiver selector: ::global_c++_name;

Limitazioni

Objective-C++ non aggiunge caratteristiche di C++ alle classi Objective-C, né aggiunge caratteristiche Objective-C a classi C++. Ad esempio, non puoi usare la sintassi Objective-C per chiamare un oggetto C++, non puoi aggiungere costruttori o distruttori per un oggetto Objective-C, e non puoi usare le parole chiave this e self intercambiabilmente. Le gerarchie di classi sono separate; una classe C++ non può ereditare da una classe Objective-C, e una classe Objective-C non può ereditare da una classe C++. Inoltre la gestione delle eccezioni multi-language non è supportata. Cioè, un'eccezione lanciata in codice Objective-C non può essere catturata nel codice C++, e un'eccezione lanciata nel codice C++ non può essere catturata nel codice Objective-C. Per più informazioni sulle eccezioni in Objective-C, consultate "Gestione delle Eccezioni".


Continua...

giovedì 6 maggio 2010

Guida Objective-C in Italiano - Messaggi Remoti - Parte 14


Eccoci alla penultima parte della Guida in Italiano alla programmazione in Objective-C, la quattordicesima per l'esattezza, nella quale parleremo di un'importante argomento che è il Remote Messaging.

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 può 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.




Remote Messaging

Come la maggior parte degli altri linguaggi di programmazione, l'Objective-C è stato inizialmente progettato per programmi che sono eseguiti come un singolo processo in un singolo spazio di indirizzi.

Tuttavia, il modello OO, dove la comunicazione prende posto tra unità relativamente auto-convenute attraverso messaggi che sono risolti a runtime, sembrerebbe ben adatto anche per la comunicazione tra processi. Non è difficile immaginare messaggi Objective-C tra oggetti che risiedono in diversi spazi di indirizzi (differenti compiti) o in diversi threads di esecuzione dello stesso compito.

Per esempio, in una tipica interazione server-client, il compito del client potrebbe inviare le sue richieste ad un oggetto designato nel server, ed il server potrebbe mirare a specifici oggetti del client per le notifiche ed altre informazioni che invia. O immaginate un'applicazione interattiva che ha bisogno di fare molti calcoli per effettuare un comando dell'utente. Potrebbe semplicemente mostrare un dialogo che dice che l'utente deve aspettare mentre è occupata, o potrebbe isolare il lavoro di processo in compiti subordinati, lasciando la parte principale dell'applicazione libera di accettare input dall'utente. Gli oggetti nei due compiti, comunicherebbero attraverso messaggi Objective-C.

Oggetti Distribuiti

I messaggi remoti in Objective-C richiedono un sistema di runtime che possa stabilire connessioni tra oggetti in diversi spazi di indirizzi, riconoscere quando un messaggio è inteso per un oggetto in uno spazio di indirizzi remoto, e trasferire i dati da uno spazio di indirizzi ad un altro. Deve anche mediare tra i programmi (schedules) separati dei due compiti; deve mantenere i messaggi finché i loro riceventi remoti siano liberi di rispondergli.

Cocoa include un'architettura ad oggetti distribuiti che è essenziale per questo tipo di estensioni del sistema di runtime. Usando gli oggetti distribuiti, puoi inviare messaggi Objective-C ad oggetti in altre attività o avere messaggi eseguiti in altri threads della stessa attività. (Quando i messaggi remoti sono inviati tra due threads della stessa attività, i threads sono trattati esattamente come threads di attività diverse). Nota che il sistema di oggetti distribuiti di Cocoa è costruito in cima al sistema di runtime; non altera il comportamento fondamentale dei tuoi oggetti Cocoa.

Per inviare un messaggio remoto, un'applicazione deve prima stabilire una connessione con il ricevente remoto. Stabilire la connessione da all'applicazione un proxy per l'oggetto remoto nel suo proprio spazio di indirizzi. Esso allora comunica con l'oggetto remoto attraverso il proxy. Il proxy assume l'identità dell'oggetto remoto; non ha identità di suo. L'applicazione è in grado di riguardare il proxy come se fosse l'oggetto remoto; Per la maggior parte degli scopi, è l'oggetto remoto.

Inviare messaggi remoti è illustrato in figura 13-1, dove l'oggetto A comunica con l'oggetto B attraverso un proxy, e i messaggi per B aspettano in una coda finché B è pronto a rispondergli.

Figura 13-1 Messaggi Remoti


Il mittente e il ricevente sono in attività differenti e sono programmate (scheduled) indipendentemente l'una dall'altra. Quindi non c'è garanzia che il ricevente sia libero di accettare un messaggio quando il mittente è pronto ad inviarlo. Quindi, i messaggi che arrivano sono posti in una coda e recuperati alla convenienza dell'applicazione ricevente.

Un proxy non agisce per conto dell'oggetto remoto o deve accedere alla sua classe. Non è una copia dell'oggetto ma un leggero sostituto per esso. In un certo senso, è trasparente; semplicemente passa i messaggi che riceve al ricevente remoto e gestisce la comunicazione tra i processi. La sua funzione principale è fornire un indirizzo locale per un oggetto che altrimenti non ne avrebbe uno. Un proxy non è totalmente trasparente. Per istanza, un proxy non ti permette di impostare e ottenere direttamente le variabili di istanza di un oggetto.

Un ricevente remoto è tipicamente anonimo. La sua classe è nascosta dentro l'applicazione remota. L'applicazione mittente non ha bisogno di conoscere come è progettata l'applicazione o che classi usa. Non ha bisogno di usare le stesse classi. Tutto ciò che gli serve sapere è a quali messaggi l'oggetto remoto risponde.

A causa di ciò, un oggetto che è designato per ricevere messaggi remoti, annuncia la sua interfaccia in un protocollo formale. Entrambe le applicazioni mittente e ricevente, dichiarano il protocollo - entrambe importano la stessa dichiarazione di protocollo. L'applicazione ricevente lo dichiara perché l'oggetto remoto deve aderire al protocollo. L'applicazione mittente lo dichiara per informare il compilatore dei messaggi che invia e per questo, potrebbe usare il metodo conformsToProtocol: e la direttiva @protocol() per testare il ricevente remoto. L'applicazione mittente non deve implementare alcuno dei metodi del protocollo; essa dichiara il protocollo solo perché il protocollo inizia i messaggi al ricevente remoto.

Language Support

I messaggi remoti emettono non solo molte intriganti possibilità per la progettazione di programmi, emettono anche alcuni interessanti problemi per il linguaggio. La maggior parte dei problemi sono collegati all'efficienza dei messaggi remoti e al grado di separazione che due attività dovrebbero mantenere mentre comunicano l'un l'altra.

Quindi quei programmatori possono dare istruzioni esplicite a riguardo l'intento di usare messaggi remoti, l'Objective-C definisce sei qualificatori di tipo che possono essere usati dichiarando metodi in un protocollo formale:
  • oneway
  • in
  • out
  • inout
  • bycopy
  • byref
Questi modificatori sono ristretti a protocolli formali; non possono essere usati in una dichiarazione di classe o di categoria. Comunque se una classe o una categoria adottano un protocollo, la sua implementazione dei metodi del protocollo può usare gli stessi modificatori che sono usati per dichiarare i metodi. La sezione seguente spiegherà come sono usati questi modificatori.

Messaggi Sincroni e Asincroni

Considerate prima un metodo con solo un semplice valore di ritorno:

- (BOOL)canDance;

Quando un messaggio canDance è inviato ad un ricevente nella stessa applicazione, il metodo è invocato ed il valore di ritorno fornito direttamente al mittente. Ma quando il ricevente è in un'applicazione remota, sono richiesti due messaggi di fondo - uno per ottenere l'oggetto remoto per invocare il metodo, e l'altro messaggio per inviare indietro il risultato del calcolo remoto. Questo è illustrato nella figura sotto:

Figura 13-2 Messaggio Round-Trip 


La maggior parte dei messaggi remoti, sono alla base, chiamate a procedure remote bi direzionali (o round-trip) come questo. L'applicazione mittente aspetta l'applicazione ricevente per invocare il metodo, completa la sua elaborazione, e invia indietro un'indicazione che ha finito, insieme con qualunque informazione di ritorno richiesta. Aspettando che il ricevente finisca, anche se non è restituita alcuna informazione, ha il vantaggio di coordinare le due applicazioni comunicanti, di tenerle entrambe "in sync". Per questo motivo, i messaggi round-trip sono spesso chiamati sincroni. I messaggi sono sincroni di default.

Comunque, non è sempre necessario o non è sempre una buona idea aspettare una risposta. A volte è sufficiente spedire i messaggi remoti e ritornare, permettendo al ricevente di ottenere la sua attività quando può. Nel frattempo, il mittente può andare avanti su altre cose. L'Objective-C fornisce un modificatore di tipo di ritorno, oneway, per indicare che un metodo è usato solo per messaggi asincroni:

- (oneway void)waltzAtWill;

Anche se oneway è un qualificatore di tipo (come const) e può essere usato in combinazione con un nome di tipo specifico, come oneway float o oneway id, l'unica combinazione che ha senso è oneway void. Un messaggio asincrono non può avere un valore di ritorno valido.

Argomenti Puntatore

Consideriamo metodi che prendono argomenti puntatore. Un puntatore può essere usato per passare informazioni al ricevente tramite riferimento. Quando invocato, il metodo guarda a cosa è memorizzato nell'indirizzo passato.

- setTune:(struct tune *)aSong

 {

      tune = *aSong;
      ...

 }

Lo stesso tipo di argomento può essere usato anche per restituire informazioni tramite riferimento. Il metodo usa il puntatore per trovare dove potrebbe mettere le informazioni richieste nel messaggio.

- getTune:(struct tune *)theSong
 {
      ...
      *theSong = tune;
 }

Il modo in cui è usato il puntatore crea una differenza in come il messaggio remoto è effettuato. In nessun caso il puntatore può semplicemente essere passato senza cambi all'oggetto remoto; esso punta a una locazione di memoria nello spazio di indirizzi del mittente e non sarebbe significativa nello spazio di indirizzi del ricevente remoto. Il sistema di runtime per i messaggi remoti deve fare alcune regolazioni dietro le scene.

Se l'argomento è usato per passare informazioni per riferimento, il sistema di runtime deve dereferenziare il puntatore, spedire il valore a cui esso punta all'applicazione remota, memorizzare il valore in un indirizzo locale di quell'applicazione e passare l'indirizzo al ricevente remoto.

Se d'altra parte, il puntatore è usato per restituire informazioni per riferimento, il valore a cui punta non deve essere inviato all'altra applicazione. Invece, un valore dall'altra applicazione deve essere rispedito indietro e scritto nella locazione indicata dal puntatore.

Nel primo caso, l'informazione è passata sulla prima "gamba" del round trip. Nel secondo caso, l'informazione è restituita sulla seconda "gamba" del round trip. Poiché questi casi risultano in molte azioni diverse sulla parte del sistema di runtime per i messaggi remoti, l'Objective-C fornisce modificatori di tipo che chiarificano le intenzioni del programmatore:
  • Il modificatore di tipo "in" indica che l'informazione è stata passata in un messaggio:


    - setTune:(in struct tune *)aSong;
  • Il modificatore "out" indica che un argomento è stato usato per restituire informazione per riferimento:


    - getTune:(out struct tune *)theSong;
  • Un terzo modificatore, "inout", indica che un argomento è usato sia per fornire informazioni sia per ri-ottenerle:


    - adjustTune:(inout struct tune *)aSong;
Il Sistema di oggetti distribuiti di Cocoa prende "inout" come modificatore di default per tutti gli argomenti puntatori tranne quelli dichiarati const, per i quali il default è "in". "inout" è l'assunzione più sicura ma anche quella che consuma più tempo dato che richiede il passaggio di informazioni in entrambe le direzioni. Il solo modificatore che ha senso per gli argomenti passati per valore (non puntatori) è "in". Mentre "in" può essere usato con qualunque tipo di argomento, "out" e "inout" hanno senso solo per i puntatori.

In C, i puntatori sono a volte usati per rappresentare valori compisiti. Per esempio, una stringa è rappresentata come un puntatore a carattere (char *). Sebbene nella notazione e implementazione c'è un livello di indirezione qui, nel concetto non c'è. Concettualmente, una stringa è un'entità di per se, non un puntatore a qualcos'altro. In casi come questi, il sistema di oggetti distribuiti dereferenzia automaticamente il puntatore e passa qualsiasi cosa puntata, per valore. Quindi, i modificatori "out" ed "inout" non hanno senso con semplici puntatori a carattere. Ci vuole un livello aggiuntivo di indirezione in un messaggio remoto per passare o restituire una stringa per riferimento:

- getTuneTitle:(out char **)theTitle;

è vero lo stesso per gli oggetti:

- adjustRectangle:(inout Rectangle **)theRect;

Queste convinzioni sono applicate a runtime, non dal compilatore.

Proxies e Copie

Finalmente, consideriamo un metodo che prende un oggetto come argomento:

- danceWith:(id)aPartner;

Un messaggio danceWith: passa un id di un oggetto al ricevente. Se il mittente ed il ricevente sono nella stessa applicazione, entrambi sarebbero in grado di riferirsi allo stesso oggetto aPartner.

Questo è vero anche se il ricevente è in un'applicazione remota, tranne se il ricevente ha bisogno di riferirsi all'oggetto attraverso un proxy (dato che l'oggetto non è nel suo spazio di indirizzi). Il puntatore che danceWith: consegna ad un ricevente remoto è di fatto un puntatore al proxy. I messaggi inviati al proxy sarebbero passati attraverso la connessione all'oggetto reale e qualsiasi informazione di ritorno sarebbe passata indietro all'applicazione remota.

Ci sono volte, quando i proxies potrebbero essere inutilmente inefficienti, quando è meglio inviare una copia dell'oggetto ad un processo remoto così che esso possa interagirvi direttamente nel suo proprio spazio di indirizzi. Per dare ai programmatori un modo per indicare che questo è inteso, l'Objective-C fornisce un modificatore di tipo bycopy:

- danceWith:(bycopy id)aClone;

bycopy può essere usato anche per i valori di ritorno:

- (bycopy)dancer;

Può essere usato in modo simile con "out" per indicare che un oggetto restituito per riferimento dovrebbe essere copiato piuttosto che consegnato nella forma di un proxy:

- getDancer:(bycopy out id *)theDancer;

<b>Nota:</b> Quando una copia di un oggetto è passata ad un'altra applicazione, non può essere anonimo. L'applicazione che riceve l'oggetto deve avere la classe dell'oggetto caricata nel suo spazio di indirizzi.

bycopy ha molto più senso per certe classi - classi che contengono una collezione di altri oggetti - spesso queste classi sono scritte in modo che una copia è inviata ad un ricevente remoto, invece del solito riferimento. Puoi sovrascrivere questo comportamento tramite byref, comunque, quindi specificando che gli oggetti passati ad un metodo o gli oggetti restituiti da un metodo dovrebbero essere passati o restituiti per riferimento. Dato che passare per riferimento è il comportamento di default per la grande maggioranza degli oggetti Objective-C, farete raramente uso della parola chiave byref.

L'unico tipo che ha senso modificare per bycopy o byrefè un oggetto, se tipato dinamicamente id o tipato staticamente da un nome di classe.

Sebbene bycopy e byref non possono essere usati in una dichiarazione di classe o categoria, possono essere usati in un protocollo formale. Ad esempio, potresti scrivere un protocollo formale foo come segue:

@Protocol foo

 - (bycopy)array;

 @end

Una classe o categoria può allora adottare il tuo protocollo foo. Questo ti permette di costruire protocolli in modo che forniscano suggerimenti su come gli oggetti dovrebbero essere passati e restituiti dai metodi descritti dal protocollo.

Fine Parte 14

Finisce qui il quattordicesimo articolo di questa Guida dedicato al Threading. Il prossimo, sarà l'ultimo, e parlerà dell'uso di codice C++ con Objective-C; quindi come utilizzare librerie C++ nelle vostre applicazioni Objective-C.

Segnalatemi eventuali errori, o commentate l'articolo se l'avete trovato utile. Iscrivetevi ai feed del blog per essere sempre aggiornati automaticamente ogni volta che saranno disponibili nuovi contenuti. Nella barra laterale del blog potete trovare l'elenco di tutti gli articoli di questa guida.
Continua...

mercoledì 5 maggio 2010

Guida Objective-C in Italiano - I Threads - Parte 13


Siamo giunti alla tredicesima parte della Guida in Italiano alla programmazione in Objective-C, oggi parleremo brevemente dei Threads.

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 può 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.


Threading

L'Objective-C fornisce supporto per la sincronizzazione dei thread e la gestione delle eccezioni. Per attivare il supporto per queste caratteristiche, usate l'interruttore -fobjc-exceptions di GNU Compiler Collection (GCC) versione 3.3 e successive.

Nota: Usando l'una o l'altra di queste caratteristiche in un programma, rende l'applicazione eseguibile solo in Mac OS X v10.3 e successivi poichè il supporto di runtime per la gestione delle eccezioni e la sincronizzazione non sono presenti in versioni precedenti del software.

Sincronizzare l'esecuzione dei Thread

L'Objective-C supportail multithreading nelle applicazioni. Questo vuol dire che i threads possono provare a modificare lo stesso oggetto allo stesso tempo, una situazione che può causare seri problemi in un programma. Per proteggere sezioni di codice dall'essere eseguite da più di un thread alla volta, l'Objective-C fornisce la direttiva @synchronized().

La direttiva @synchronized()chiude una sezione di codice per l'uso da un singolo thread. Gli altri threads sono bloccati finchè il thread esce dal codice protetto; che è, quando l'esecuzione continua dopo l'ultimo statement del blocco @synchronized().

La direttiva @sinchronized prende come suoi soli argomenti qualunque oggetto Objective-C incluso self. Quest'oggetto è conosciuto come semaforo di esclusione reciproca o mutex. Esso permette ad un thread di bloccare una sezione di codice per prevenirne l'uso da altri threads. Dovresti separare i semafori per proteggere diverse sezioni critiche di un programma. è più sicuro creare tutti oggetti mutuamente esclusivi prima che l'applicazione diventi multithreaded per evitare condizioni di competizione.

Il listato 12-1 mostra un esempio di codice che usa self come il mutex per sincronizzare l'accesso a metodi di istanza dell'oggetto corrente. Puoi prendere un approccio simile per sincronizzare i metodi di classe della classe associata, usando l'oggetto Class al posto di self. Nell'ultimo caso, ovviamente, solo un thread alla volta può eseguire un metodo di classe perchè c'è solo un oggetto di classe che è condiviso da tutti i chiamanti.

Listato 12-1 Bloccare un metodo usando self
- (void)criticalMethod

{

@synchronized(self) {

// Critical code.

...

}

}

Il listato 12-2 mostra un approccio generale. Prima di eseguire un processo critico, il codice ottiene un semaforo dalla classe Account e lo usa per chiudere sezioni critiche. La classe Account potrebbe creare il semaforo nel suo metodo initialize.

Listato 12-2 Bloccare un metodo usando un semaforo personalizzato
Account *account = [Account accountFromString:[accountField stringValue]];



// Get the semaphore.

id accountSemaphore = [Account semaphore];



@synchronized(accountSemaphore) {

// Critical code.

...

}

La caratteristica di sincronizzazione dell'Objective-C supporta codice ricorsivo e rientrante. Un thread può usare un singolo semaforo molte volte, in una maniera ricorsiva; altri threads sono bloccati dall'uso del semaforo finché il thread rilascia tutte le parti di codice bloccate ottenute da lui; che sarebbe, quando ogni blocco @synchronized() termina normalmente o tramite un'eccezione.

Quando il codice in un blocco @synchronized() lancia un'eccezione, il runtime cattura l'eccezione, rilascia il semaforo (così che il codice protetto possa essere eseguito da altri threads), e ri-lancia l'eccezione al prossimo gestore di eventi.

Fine Parte 13

Finisce qui il tredicesimo articolo di questa Guida dedicato al Threading. Siamo ormai quasi alla fine, e il prossimo post tratterà i messaggi remoti.

Segnalatemi eventuali errori, o commentate l'articolo se l'avete trovato utile, anche per incentivarmi a continuare a pubblicare le mie traduzioni. Iscrivetevi ai feed del blog per essere sempre aggiornati automaticamente ogni volta che sono disponibili nuovi contenuti.
Nella barra laterale del blog potete trovare l'elenco di tutti gli articoli di questa guida.

Continua...

martedì 4 maggio 2010

Guida Objective-C in Italiano - Le Eccezioni - Parte 12


Siamo giunti alla dodicesima parte della Guida in Italiano alla programmazione in Objective-C, oggi parleremo della Gestione delle Eccezioni. Vi ricordo che potrete trovare tutti i links agli articoli di questa guida nella barra laterale del sito.

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 può 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.



Gestione delle Eccezioni

Il linguaggio Objective-C ha una sintassi per la gestione delle eccezioni simile a quella dei linguaggi Java and C++. Accoppiata con l'uso di NSException, NSError, o classi personalizzate, puoi aggiungere una robusta gestione degli errori per i tuoi programmi. Questo articolo fornisce un sommario della sintassi delle eccezioni e la loro gestione.

Abilitare la gestione delle Eccezioni

Usando gcc versione 3.3 e successive, Objective-C fornisce un supporto per la gestione delle eccezioni. Per attivare il supporto per queste caratteristiche usa l'interruttore -fobjc-exceptions del GNU Compiler Collection (GCC) versione 3.3 e successivo. (Nota che questo rende l'applicazione eseguibile solo su Mac OS X v10.3 e successivi poiché il supporto di runtime per la gestione delle eccezioni e la sincronizzazione non è presente in versioni precedenti del software).

Gestione Eccezioni

Un'eccezione è una condizione speciale che interrompe il normale flusso d'esecuzione del programma. Ci sono una varietà di motivi per cui un'eccezione potrebbe essere generata dall'hardware così come dal software. Esempi includono errori aritmetici come divisioni per zero, uderflow o overflow, chiamate a istruzioni indefinite, e tentare di accedere ad una collezione di elementi fuori dai suoi limiti.

Il supporto delle eccezioni Objective-C gira attorno a quattro direttive di compilazione: @try, @catch, @throw, e @finally:
  • Il codice che può lanciare potenzialmente un'eccezione è racchiuso in un blocco @try.
  • Un blocco @catch() contiene la gestione logica delle eccezioni per le eccezioni lanciate in un blocco @try. Puoi avere blocchi @catch() multipli per catturare diversi tipi di eccezioni.
  • Un blocco @finally contiene codice che deve essere eseguito se un'eccezione è o non è lanciata.
  • Usi la direttiva @throw per lanciare un'eccezione, che è essenzialmente un'oggetto Objective-C. Tipicamente usi un'oggetto NSException, ma non è richiesto.
L'esempio sotto descrive un semplice algoritmo di gestione delle eccezioni:
Cup *cup = [[Cup alloc] init];

@try {
     [cup fill];
}

 @catch (NSException *exception) {
      NSLog(@"main: Caught %@: %@", [exception name], [exception reason]);
 }

 @finally {
      [cup release];
 }

Catturare Tipi Diversi di Eccezioni

Per catturare un'eccezione lanciata in un blocco @try, usa uno o più blocchi @catch() subito dopo il blocco @try. I blocchi @catch() dovrebbero essere ordinati dal più specifico al meno specifico. In questo modo potete adattare il processo di eccezioni come gruppi, come mostrato nel listato 11-1.

Listato 11-1 Un gestore di eccezioni
@try {
      ...
 }

@catch (CustomException *ce) { // 1
      ...
 }

@catch (NSException *ne) { // 2
      // si eseguono le elaborazioni necessarie a questo livello.
      ...
 }

@catch (id ue) {
      ...
 }

@finally { // 3
      // si eseguono le elaborazini necessarie se è occorsa o meno un'eccezione
      ...
}

La lista seguente descrive le linee di codice numerate:
  1. Cattura il tipo di eccezione più specifica.
  2. Cattura un tipo di eccezione più generico.
  3. Esegue ogni elaborazione che deve essere eseguita se un'eccezione è lanciata o meno.

Lanciare Eccezioni

Per lanciare un'eccezione devi istanziare un oggetto con l'informazione appropriata, come il nome dell'eccezione e il motivo per cui è stata lanciata.
NSException *exception = [NSException exceptionWithName:@"HotTeaException"
     reason:@"The tea is too hot" userInfo:nil];

@throw exception;
Importante: In molti ambienti, usare eccezioni è ragionevolmente ordinario. Ad esempio, potresti lanciare un'eccezione per segnalare che una routine potrebbe non eseguirsi normalmente - come quando un file manca o i dati non possono essere analizzati correttamente. Le eccezioni consumano intensivamente risorse in Objective-C. Non dovresti usare eccezioni per controlli generali del flusso, o semplicemente per significare errori. Invece dovresti usare i valori di ritorno di un metodo o di una funzione per indicare che è occorso un errore, e fornire informazioni riguardo il problema in un oggetto errore.

In un blocco @catch(), puoi re-lanciare l'eccezione catturata usando la direttiva @throw senza un argomento. Questo può aiutare a rendere il codice più leggibile.

Non sei limitato a lanciare oggetti NSException. Puoi lanciare qualsiasi oggetto Objective-C come un oggetto eccezione. La classe NSException fornisce i metodi che aiutano l'elaborazione dell'eccezione, ma puoi implementare la tua propria classe se vuoi. Puoi anche creare una sottoclasse di NSEdception per implementare tipi di eccezioni, come le eccezioni di file system o eccezioni di comunicazione.

Fine Parte 12

Finisce qui il dodicesimo articolo di questa Guida dedicato alla gestione delle eccezioni nel linguaggio di programmazione Objective-C. Il prossimo post di questa guida, parlerà brevemente del Threading. 

Segnalatemi eventuali errori, e commentate l'articolo se l'avete trovato utile, anche per incentivarmi a continuare a pubblicare le mie traduzioni. Iscrivetevi ai feed del blog per essere sempre aggiornati automaticamente ogni volta che sono disponibili nuovi contenuti. Nella barra laterale del blog potete trovare l'elenco di tutti gli articoli di questa guida.
Continua...

lunedì 3 maggio 2010

Guida Objective-C in Italiano - Selettori - Parte 11


Oggi parleremo dei Selettori del linguaggio Objective-C; vi ricordo che questa è l'undicesima parte della Guida in Italiano alla programmazione in Objective-C, e che nella barra laterale del sito potrete trovare tutti i links agli articoli di questa guida.

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 può 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.


Selettori

In Objective-C, la parola selettore ha due significati. Può essere usata per riferirsi semplicemente al nome di un metodo quando è usato in un messaggio ad un oggetto nel codice sorgente. Si può anche riferire all'identificatore univoco che rimpiazza il nome quando il codice sorgente è compilato. i selettori compilati sono di tipo SEL. Tutti i metodi con lo stesso nome hanno lo stesso selettore. Puoi usare un selettore per invocare un metodo su un oggetto - questo fornisce le basi per l'implementazione del pattern di progettazione target-azione in Cocoa.

Metodi e Selettori

Per efficienza, i nomi full ASCII non sono usati come selettori di metodo nel codice compilato. Invece, il compilatore scrive ogni nome di metodo in una tabella e poi accoppia il nome con un identificatore univoco che rappresenta il metodo a runtime. Il sistema di runtime si assicura che ogni identificatore sia univoco: non ci sono due selettori che sono uguali, e tutti i metodi con lo stesso nome hanno lo stesso selettore.

SEL e @selector

I selettori compilati sono assegnati a un tipo speciale, SEL, per distinguerli dagli altri dati. I selettori validi non sono mai 0. Devi permettere al sistema di assegnare identificatori SEL ai metodi; è futile assegnarli arbitrariamente.

La direttiva @selector() ti permette di riferirti ai selettori compilati, piuttosto che al completo nome del metodo. Qui, il selettore per setWidth:height: è assegnato alla variabile setWidthHeight:
SEL setWidthHeight;

setWidthHeight = @selector(setWidth:height:);

è più efficiente assegnare valori a variabili SEL a tempo di compilazione con la direttiva @selector(). Comunque, in alcuni casi, potresti aver bisogno di convertire una stringa di caratteri ad un selettore a runtime. Puoi farlo con la funzione NSSelectorFromString:
setWidthHeight = NSSelectorFromString(aBuffer);
Le conversioni sono possibili anche nella direzione opposta. La funzione NSStringFromSelector restituisce un nome di metodo per un selettore:
NSString *method;

method = NSStringFromSelector(setWidthHeight);

Metodi e Selettori

Selettori compilati identificano nomi di metodi, non implementazioni di metodi. Il metodo display per una classe, ad esempio, ha lo stesso selettore di un metodo display definito nelle altre classi. Questo è essenziale per il polimorfismo e il collegamento dinamico; permette di inviare lo stesso messaggio a riceventi appartenenti a classi diverse. Se ci fosse un selettore per ogni implementazione di metodo, un messaggio non sarebbe differente da una chiamata di funzione.

Un metodo di classe e un metodo di istanza con lo stesso nome sono assegnati allo stesso selettore. Comunque, a causa dei loro domini separati, non c'è confusione tra i due. Una classe potrebbe definire un metodo di classe display in aggiunta al metodo d'istanza display.

Valori di Ritorno e Tipi di Argomenti dei Metodi

La routine di messaggistica ha accesso alle implementazioni dei metodi solo attraverso i selettori, quindi li tratta allo stesso modo tutti i metodi con lo stesso selettore. Scopre i tipi restituiti di un metodo, e i tipi di dati dei suoi argomenti, dal selettore. Quindi, eccetto per messaggi inviati a riceventi tipati staticamente, il collegamento dinamico richiede che tutte le implementazioni dei metodi con lo stesso nome debbano avere lo stesso valore di ritorno e tipi di argomenti. (riceventi staticamente tipati sono un eccezione a questa regola, dato che il compilatore può imparare l'implementazione del metodo dal tipo della classe).

Sebbene i metodi di classe con lo stesso nome e i metodi di istanze siano rappresentati dallo stesso selettore, possono avere argomenti diversi e tipi di ritorno diversi.

Variare il Messaggio a Runtime

I metodi performSelector:, performSelector:withObject:, e performSelector:withObject:withObject:, definiti nel protocollo NSObject, prendono gli identificatori SEL come loro argomenti iniziali. Tutti e tre i metodi mappano direttamente nella funzione di messaggistica. Per esempio:
[friend performSelector:@selector(gossipAbout:)

withObject:aNeighbor];

è equivalente a:
[friend gossipAbout:aNeighbor];
Questi metodi rendono possibile variare un messaggio a runtime, come è possibile variare l'oggetto che riceve il messaggio. I nomi di variabili possono essere usati in entrambe le metà di un'espressione di un messaggio:
id helper = getTheReceiver();

SEL request = getTheSelector();

[helper performSelector:request];

In questo esempio, il ricevente (helper) è scelto a runtime ( dalla funzione fittizia getTheReceiver), e il metodo che il ricevente è chiesto di eseguire (request) è anch'esso determinato a runtime (dalla funzione ugualmente fittizia getTheSelector).

Nota: performSelector: e i suoi metodi compagni restituiscono un id. Se il metodo che è eseguito restituisce un tipo diverso, dovrebbe essere fatto un cast al tipo appropriato. (comunque, fare il cast non funziona per tutti i tipi; il metodo dovrebbe restituire un puntatore o un tipo compatibile con un puntatore).

Il Design Pattern Target-Azione

Nel suo trattamento di controlli di interfacce utente, l'Application Kit fa buon uso dell'abilità di variare sia ricevente che messaggio.

Oggetti NSControl sono graficamente dispositivi che possono essere usati per dare istruzioni ad un'applicazione. La maggior parte somigliano a dispositivi di controllo del mondo reale come bottoni, interruttori, campi di testo, menu ecc... Nel software questi dispositivi si trovano tra applicazione e utente. Essi interpretano gli eventi che vengono dai dispositivi hardware come la tastiera ed il mouse e li traducono in istruzioni specifiche per l'applicazione. Per esempio, un pulsante etichettato "Find" tradurrebbe un click del mouse in un'istruzione per l'applicazione per iniziare a cercare qualcosa.

L'Application Kit definisce un modello per creare dispositivi di controllo e di suo, definisce pochi dispositivi immediatamente disponibili. Ad esempio, la classe NSButtonCell definisce un oggetto che puoi assegnare ad un'istanza di NSMatrix ed inizializzare con una taglia, un'etichetta, un'immagine, un carattere, e una tastiera alternativa. Quando l'utente clicca sul pulsante (o usa la tastiera alternativa), l'oggetto NSButtonCell invia un messaggio che istruisce l'applicazione a fare qualcosa. Per fare questo, un oggetto NSButtonCell deve essere inizializzato non solo con un'immagine, dimensione ed etichetta ma con direzioni su che messaggio inviare e a chi inviarlo. Di conseguenza un'istanza di NSButtonCell può essere inizializzata per un messaggio d'azione, il selettore del metodo che dovrebbe usare nel messaggio che invia, ed il target, l'oggetto che dovrebbe ricevere il messaggio.
[myButtonCell setAction:@selector(reapTheWind:)];

[myButtonCell setTarget:anObject];

Il pulsante cella invia il messaggio usando il metodo performSelector:withObject: di NSObject. Tutti i messaggi d'azione prendono un singolo argomento, l'id del dispositivo di controllo che invia il messaggio.

Se Objective-C non permettesse ai messaggi di essere variati, tutti gli oggetti NSButtonCell dovrebbero inviare lo stesso messaggio; il nome del metodo dovrebbe essere congelato nel codice sorgente di NSButtonCell. Invece di implementare semplicemente un meccanismo per traslare le azioni dell'utente in messaggi d'azione, i pulsanti celle e altri controlli dovrebbero vincolare il contenuto del messaggio. Questo renderebbe difficile per qualunque oggetto rispondere a più di un button cell. Ci dovrebbe anche essere un unico target per un pulsante, o l'oggetto target dovrebbe scoprire da quale bottone viene il messaggio e agire di conseguenza.

Ogni volta che riorganizzi l'interfaccia utente, dovresti anche re-implementare il metodo che risponde al messaggio azione. Questa sarebbe una complicazione non necessaria che l'Objective-C evita felicemente.

Evitare Errori nei Messaggi

Se un oggetto riceve un messaggio per eseguire un metodo che non è nel suo repertorio, risulterà un errore. è la stessa sorta di errore di chiamare una funzione non esistente. Ma poiché i messaggi occorrono a runtime, l'errore spesso non è evidente finché il programma non viene eseguito.

è relativamente semplice evitare questi errori quando il selettore del messaggio è costante e la classe dell'oggetto ricevente è conosciuta. Come scrivi i tuoi programmi, puoi accertarti che il ricevente sia in grado di rispondere. Se il ricevente è tipato staticamente, il compilatore esegue questo test per te.

Comunque, se il selettore dl messaggio o la classe del ricevente varia, potrebbe essere necessario post-porre questo test fino a runtime. Il metodo respondsToSelector:, definito nella classe NSObject, determina se un ricevente può rispondere ad un messaggio. Prende il selettore di metodo come un argomento e restituisce se il ricevente ha accesso a metodi che combaciano con il selettore:
if ( [anObject respondsToSelector:@selector(setOrigin::)] )

[anObject setOrigin:0.0 :0.0];

else

fprintf(stderr, "%s can’t be placed\n",

[NSStringFromClass([anObject class]) UTF8String]);

Il test respondsToSelector: è specialmente importante quando si inviano messaggi ad oggetti di cui non hai il controllo al tempo di compilazione. Ad esempio, se scrivi codice che invia un messaggio ad un oggetto rappresentato da una variabile che altri possono impostare, dovresti accertarti che il ricevente implementi un metodo che risponda al messaggio.

Nota: Un oggetto si può anche sistemare per avere i messaggi che riceve, inoltrati ad altri oggetti se esso non può rispondere direttamente ai messaggi da solo. In tal caso, appare che l'oggetto può gestire il messaggio, anche se l'oggetto risponde al messaggio indirettamente assegnandolo ad un altro oggetto.

Fine Parte 11

Finisce qui l'undicesimo articolo di questa Guida dedicato ai Selettori del linguaggio di programmazione Objective-C. Il prossimo post di questa guida, parlerà della Gestione delle Eccezioni.

Segnalatemi eventuali errori, o commentate l'articolo se l'avete trovato utile, anche per incentivarmi a continuare a pubblicare le mie traduzioni. Iscrivetevi ai feed del blog per essere sempre aggiornati automaticamente ogni volta che sono disponibili nuovi contenuti.
Nella barra laterale del blog potete trovare l'elenco di tutti gli articoli di questa guida.


Continua...

domenica 2 maggio 2010

Guida Objective-C in Italiano - Comportamenti Statici - Parte 10


Nel decimo capitolo della Guida in Italiano alla programmazione in Objective-C, parleremo approfonditamente della tipatura statica, abilitare comportamenti statici rispetto a quelli che in Objective-C sono dinamici di default.

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 può 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.


Abilitare Comportamenti Statici

Questo capitolo spiega come funziona la tipatura statica e parla di alcune altre caratteristiche dell'Objective-C, incluso metodi per sovrastare temporaneamente il suo dinamismo inerente.

Comportamento Dinamico di Default

Da progettazione, gli oggetti Objective-C sono entità dinamiche. Quante più decisioni possibili su di loro sono spinte dal tempo di compilazione al tempo di esecuzione:
  • La memoria per gli oggetti è allocata dinamicamente a runtime dai metodi di classe che creano nuove istanze.
  • Gli Oggetti sono tipati dinamicamente. Nel codice sorgente (a tempo di compilazione), ogni variabile di un oggetto può essere tipata id, non importa di che classe sia l'oggetto. L'esatta classe di una variabile id (e quindi i suoi metodi particolari e la struttura dati) non è determinata finché il programma non è in esecuzione.
  • Messaggi e metodi sono legati dinamicamente. Una procedura di runtime fà combaciare il selettore di metodo nel messaggio con un'implementazione di metodo che appartiene al ricevente.
Queste caratteristiche danno ai programmi OO una grande flessibilità, ma c'è un prezzo da pagare. In particolare, il compilatore non può controllare i tipi esatti (classi) delle variabili id. Per permettere un miglior controllo di tipo a tempo di compilazione, e per rendere il codice più auto-documentante, Objective-C permette agli oggetti di essere tipati staticamente con un nome di classe piuttosto che la tipatura generica come id. Permette anche di spegnere alcune delle caratteristiche OO per spostare operazioni dal tempo di esecuzione a quello di compilazione.

Nota: I messaggi sono qualcosa di più lento delle chiamate a funzioni, tipicamente incorrono in una quantità insignificante di lavoro aggiuntivo comparati al lavoro attualmente eseguito. Il caso particolarmente raro in cui bypassare il dinamismo dell'Objective-C potrebbe essere permesso, può essere provato usando gli strumenti di analisi come Shark o Instruments.

Tipatura Statica

Se un puntatore ad un nome di classe è usato al posto di un id in una dichiarazione di oggetto,
Rectangle *thisObject;
Il compilatore restringe il valore della variabile dichiarata ad essere o un'istanza della classe nominata nella dichiarazione o un'istanza di una classe che eredita dalla classe nominata. Nell'esempio sopra, ThisObject può essere solo un Rectangle di un qualche tipo.

Tipare staticamente oggetti ha la stesse strutture dati interne degli oggetti dichiarati id. Il tipo non influenza l'oggetto; influenza dolo la quantità di informazione data al compilatore circa l'oggetto e la quantità di informazione disponibile a quelli che leggono il codice sorgente.

La tipatura statica non influenza neanche il modo in cui l'oggetto è trattato a runtime. Gli oggetti staticamente tipati sono allocati dinamicamente dagli stessi metodi di classe che creano le istanze di tipo id. Se Square (quadrato) è una sottoclasse di Rectangle, il seguente codice produrrà ancora un oggetto con tutte le variabili di istanza di uno Square, non solo quelle di un Rectangle:
Rectangle *thisObject = [[Square alloc] init];
I messaggi inviati ad oggetti tipati staticamente sono legati dinamicamente, proprio come per gli oggetti tipati id. Il tipo esatto di un ricevente tipato staticamente è determinato ancora a runtime come una parte del processo di messaggistica. Un messaggio display inviato a thisObject
[thisObject display];
esegue la versione del metodo definita nella classe Square, non in quella della sua superclasse Rectangle.

Dando al compilatore più informazioni su un oggetto, la tipatura statica apre le possibilità che siano queste info siano assenti per oggetti tipati id:
  • In alcune situazioni, permette i controlli di tipo a tempo di compilazione.
  • Può liberare oggetti dalla restrizione che i metodi nominati identicamente devono avere identici valori di ritorno e tipi di argomenti.
  • Permette di usare la struttura operatore punto per accedere direttamente alle variabili d'istanza di un oggetto.
I primi due argomenti sono discussi nella sezione che segue. Il terzo è spiegato nel secondo articolo di questa guida "Definire Classi".

Controlli di Tipo

Con l'informazione aggiuntiva fornita dalla tipatura statica, il compilatore può consegnare un servizio di controllo di tipi migliore in due situazioni:
  1. Quando un messaggio è inviato a un ricevente tipato staticamente, il compilatore può assicurarsi che il ricevente può rispondere. Un warning è emesso se il ricevente non ha accesso al metodo nominato nel messaggio.
  2. Quando un oggetto tipato staticaemente è assegnato a una variabile tipata staticamente, il compilatore si accerta che i tipi siano compatibili. Emette un warning se non lo sono.
Un assegnamento può essere fatto senza warning, se la classe dell'oggetto definito è identica a, o eredita da, la classe della variabile che riceve l'assegnamento. Il seguente esempio illustra questo:
Shape *aShape;

Rectangle *aRect;

aRect = [[Rectangle alloc] init];

aShape = aRect;
Qui aRect può essere assegnato a aShape perchè un Rectangle è un tipo di Shape - la classe Rectangle eredita da Shape. Comunque, se i ruoli delle due variabili sono invertiti e aShape è assegnata a aRect, il compilatore genera un warning; non tutte le forme sono Rettangoli.

Non c'è controllo quando l'espressione su uno dei due lati dell'assegnamento è un id. Un oggetto tipato staticamente può essere liberamente assegnato ad un id, o un id a un oggetto tipato staticamente. Perché i metodi come alloc ed init restituiscono variabili tipate staticamente. Il seguente codice è incline ad errori, ma ciò nonostante si concede:
Rectangle *aRect;

aRect = [[Shape alloc] init];

Tipi di Ritorno e degli Argomenti

In generale, i metodi in classi diverse che hanno lo stesso selettore (lo stesso nome) devono anche condividere gli stessi tipi di ritorno e tipi di argomenti. Questo vincolo è imposto dal compilatore per permettere il collegamento dinamico (dynamic binding). Poiché la classe di un messaggio ricevente, (e quindi dettagli specifici di classe riguardo al metodo che gli è chiesto di eseguire) non può essere conosciuta a tempo di compilazione, il compilatore deve trattare allo stesso modo tutti i metodi con lo stesso nome. Quando prepara le informazioni sul ritorno del metodo e sui tipi degli argomenti per il sistema di runtime, crea solo una descrizione di metodo per ogni selettore di metodo.

Comunque, quando un messaggio è inviato a un oggetto tipato staticamente, la classe del ricevente è conosciuta dal compilatore. Il compilatore ha accesso a informazioni specifiche della classe riguardo ai metodi. Quindi, il messaggio è liberato dalle restrizioni sui suoi valori di ritorno e tipi di argomenti.

Tipatura Statica a una Classe Ereditata

Un'istanza può essere tipata staticamente alla sua propria classe o a qualunque classe da cui eredita. Tutte le istanze, ad esempio, possono essere tipate staticamente come NSObject.

Comunque, il compilatore capisce la classe di un oggetto tipato staticamente solo dal nome di classe nella designazione del tipo, ed effettua il suo controllo di tipi di conseguenza. Tipare un'istanza a una classe ereditata può quindi risultare in discrepanze tra ciò che il compilatore pensa che potrebbe accadere a runtime e cosa realmente succede.

Ad esempio, se tipi staticamente un'istanza di Rectangle come una Shape,
Shape *myRectangle = [[Rectangle alloc] init];
Il compilatore la tratterà come una Shape. Se invii all'oggetto un messaggio per eseguire un metodo di Rectangle,
BOOL solid = [myRectangle isFilled];
Il compilatore si lamenterà. Il metodo isFilled è definito nella classe Rectangle ma non in Shape.

Comunque, se gli invii un messaggio per eseguire un metodo che classe Shape conosce,

[myRectangle display];

Il compilatore non si lamenterà, anche se Rectangle sovrascrive il metodo. A runtime, è eseguita la versione del metodo di Rectangle.

In modo analogo, supponiamo che la classe sorastante dichiara un metodo worry che restituisce un double,
- (double)worry;
e la sottoclasse di mezzo sovrascrive il metodo e dichiara un nuovo tipo di ritorno:
- (int)worry;

Se un'istanza è tipata staticamente alla classe superiore, il compilatore penserà che il suo metodo worry restituisce un double, e se un istanza è tipata alla classe di mezzo, penserà che worry restituisce un intero. Risulteranno ovviamente errori se un'istanza della classe di mezzo è tipata alla classe superiore. Il compilatore informerà il sistema di runtime che un messaggio worry è inviato ad un oggetto che restituisce un double, ma a runtime esso restituisce attualmente un int e genera un errore.

La tipatura statica può liberare metodi nominati in modo identico dalla restrizione che devono avere valori di ritorno e tipi di argomento identici, ma lo può fare così affidabilmente solo se i metodi sono dichiarati in rami differenti della gerarchia delle classi.

Fine Parte 10

Finisce qui il decimo articolo di questa Guida dedicato alla tipatura statica nel linguaggio di programmazione Objective-C. Il prossimo post di questa guida, parlerà dei Selettori.

Segnalatemi eventuali errori, o commentate l'articolo se l'avete trovato utile, anche per incentivarmi a continuare a pubblicare le mie traduzioni. Iscrivetevi ai feed del blog per essere sempre aggiornati automaticamente ogni volta che sono disponibili nuovi contenuti. Nella barra laterale del blog potete trovare l'elenco di tutti gli articoli di questa guida.
Continua...

sabato 1 maggio 2010

Guida Objective-C in Italiano - Parte 9 - Enumerazione Veloce

Enumerare oggetti di una collezione, tramite la caratteristica Enumerazione Veloce; questo sarà l'argomento trattato oggi, in questa nona parte della Guida in Italiano alla programmazione in Objective-C.

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 può 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.



Enumerazione Veloce

L'enumerazione veloce è una caratteristica del linguaggio che ti permette di enumerare in modo sicuro ed efficiente i contenuti di una collezione usando una sintassi efficiente.

La caratteristica for…in

L'enumerazione veloce è una caratteristica del linguaggio che ti permette di enumerare in modo sicuro ed efficiente i contenuti di una collezione usando una sintassi efficiente. La sintassi è definita come segue:
for ( Tipo nuovaVariabile in espressione ) { statements }
o
Tipo nuovaVariabile;

for ( nuovaVariabile in expression ) { statements }

In entrambi i casi, le espressioni raccolgono un oggetto conforme al protocollo NSFastEnumeration. La variabile che itera è impostata a turno ad ogni elemento nell'oggetto restituito, e il codice definito dagli statements è eseguito. La variabile che itera è impostata a nil quando il ciclo finisce esaurendo la sorgente del gruppo di oggetti. Se il ciclo è terminato presto, la variabile che itera è lasciata a puntare all'ultimo elemento dell'iterazione.

Ci sono molti vantaggi dell'uso dell'enumerazione veloce:
  • L'enumerazione è considerevolmente più efficiente rispetto ad usare direttamente NSEnumerator.
  • La sintassi è concisa.
  • L'enumerazione è sicura - l'enumeratore ha una sentinella di mutazione, così che provi a modificare la collezione durante l'enumerazione, verrà emessa un'eccezione.
Dato che la mutazione dell'oggetto durante l'iterazione è vietata, puoi eseguire anche enumerazioni multiple contemporaneamente.

Adottare l'enumerazione veloce

Qualsiasi classe le cui istanze forniscono un accesso ad una collezione di altri oggetti può adottare il protocollo NSFastEnumeration. La collezione di classi Cocoa - NSArray, NSDictionary, e NSSet - adottano questo protocollo, come fà NSEnumerator. Dovrebbe essere ovvio che nei casi di NSArray e NSSet l'enumerazione riguarda i loro contenuti. Per altre classi, la documentazione corrispondente potrebbe chiarire che proprietà è iterata - ad esempio, NSDictionary e la classe NSManageObjectModel del Core Data forniscono supporto per l'enumerazione veloce; NSDictionary numera le sue chiavi, e NSManageObjectModel enumera le sue entità.

Usare l'enumerazione veloce

Il seguente codice d'esempio illustra l'uso dell'enumerazione veloce con oggetti NSArray e NSDictionary.
NSArray *array = [NSArray arrayWithObjects:
     @"One", @"Two", @"Three", @"Four", nil];

 for (NSString *element in array) {
     NSLog(@"element: %@", element);
 }

 NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
     @"quattuor", @"four", @"quinque", @"five", @"sex", @"six", nil];

 NSString *key;
 for (key in dictionary) {
      NSLog(@"English: %@, Latin: %@", key, [dictionary valueForKey:key]);
 }

Puoi anche usare oggetti NSEnumerator con l'enumerazione veloce, come illustrato nel seguente esempio:
NSArray *array = [NSArray arrayWithObjects:
     @"One", @"Two", @"Three", @"Four", nil];

 NSEnumerator *enumerator = [array reverseObjectEnumerator];
 for (NSString *element in enumerator) {
      if ([element isEqualToString:@"Three"]) {
           break;
      }
 }

 NSString *next = [enumerator nextObject];
 // next = "Two"

Per collezioni o enumeratori che hanno un ordine ben definito - come NSArray o istanze di NSEnumerator derivate da un array - l'enumerazione procede in quell'ordine, quindi, per conoscere l'indice dell'elemento della collezione che vi serve, vi basterà contare le iterazioni.
NSArray *array = /* assume this exists */;

 NSUInteger index = 0;

 for (id element in array) {
      NSLog(@"Element at index %u is: %@", index, element); index++;
 }

In altri rispetti, la caratteristica si comporta come un ciclo for standard. Puoi usare break per interrompere l'iterazione; e se vuoi saltare elementi puoi usare uno statement condizionale annidato come mostrato nel seguente esempio:
NSArray *array = /* assumiamo che questo esista */;

 for (id element in array) {
      if (/* qualche test per l'elemento */) {
           // statements che si applicano ai soli elementi che superano il test
      }
 }

Se vuoi saltare il primo elemento e poi processare non più di cinque ulteriori elementi, potresti fare come mostrato in questo esempio:
NSArray *array = /* assumiamo che questo esista */;
NSUInteger index = 0;

 for (id element in array) {
      if (index != 0) {
           NSLog(@"Element at index %u is: %@", index, element);
      }
      if (++index >= 6) {
           break;
      }
 }

Fine Parte 9

Finisce qui il nono articolo di questa Guida dedicato all'Enumerazione Veloce del linguaggio di programmazione Objective-C. Il prossimo post di questa guida, parlerà di come abilitare comportamenti statici; vedremo quindi più in dettaglio come funziona la tipatura statica, e discuteremo di alcune caratteristiche dell'Objective-C come vari modi per sormontare temporaneamente il suo dinamismo.

Segnalatemi eventuali errori, o commentate l'articolo se l'avete trovato utile, anche per incentivarmi a continuare a pubblicare le mie traduzioni. Iscrivetevi ai feed del blog per essere sempre aggiornati automaticamente ogni volta che sono disponibili nuovi contenuti.
Nella barra laterale del blog potete trovare l'elenco di tutti gli articoli di questa guida.
Continua...

venerdì 30 aprile 2010

Guida Objective-C in Italiano - Parte 8 - Riferimenti Associativi


Questa è l'ottava parte della Guida alla programmazione in Objective-C, oggi parleremo di Riferimenti Associativi.

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 può 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.




Riferimenti Associativi

Si usano i riferimenti associativi per simulare l'aggiunta di variabili di istanza di un oggetto ad una classe esistente.

I riferimenti associativi sono disponibili solo in Mac OS X v10.6 e successivi.

Aggiungere Memoria fuori a una Definizione di Classe

Usando i riferimenti associativi, puoi aggiungere memoria ad un oggetto senza modificare la dichiarazione di classe. Questo può essere utile se non hai accesso al codice sorgente della classe, o se per motivi di compatibilità binaria non puoi alterare il layout dell'oggetto.

Le Associazioni sono basate su una chiave, quindi per ogni oggetto puoi aggiungere quante associazioni vuoi, ognuna usando una chiave diversa. Un'associazione può anche assicurare che l'oggetto associato rimane valido per almeno il tempo di vita dell'oggetto sorgente (senza la possibilità di introdurre cicli non riscuotibili in un ambiente garbage-collected).

Creare Associazioni

Usate la funzione Objective-C di runtime objc_setAssociatedObject per creare un'associazione tra un oggetto ed un altro. La funzione prende quattro argomenti: l'oggetto sorgente, una chiave, il valore e una politica di associazione costante. Di queste, la chiave e la politica di associazione meritano ulteriori discussioni.
  • La chiave è un puntatore a void. La chiave per ogni associazione deve essere unica. Un pattern tipico è usare una variabile statica.
  • La politica specifica se l'oggetto associato è assegnato, conservato o copiato (assign, retain, copy) e se l'associazione è fatta atomicamente o non-atomicamente. Questo segue un pattern simile agli attributi di una proprietà dichiarata. Specifica la politica per la relazione usando una costante (vedi objc_AssociationPolicy).
Il seguente esempio mostra come puoi stabilire un'associazione tra un array ed una stringa.
Listato 7-1 Stabilire un'associazione tra un array e una stringa



static char overviewKey;

NSArray *array = [[NSArray alloc] initWithObjects:@"One", @"Two", @"Three", nil];

// For the purposes of illustration, use initWithFormat: to ensure the string can be deallocated

NSString *overview = [[NSString alloc] initWithFormat:@"%@", @"First three numbers"];

objc_setAssociatedObject(array, &overviewKey, overview, OBJC_ASSOCIATION_RETAIN);

[overview release];

// (1) overview valid

[array release];

// (2) overview invalid

Al punto (1), la stringa overview è ancora valida poiché la politica OBJC_ASSOCIATION_RETAIN specifica che l'array conserva l'oggetto associato. Quando l'array è deallocato, (al punto 2), overview è rilasciata e in questo caso quindi, anche deallocata. Se provi, ad esempio, ad annotare il valore di overview, genererai un'eccezione di runtime.



Recuperare Oggetti Associati

Puoi recuperare un oggetto associato usando la funzione di runtime di Objective-C objc_getAssociatedObject. Continuando l'esempio mostrato nel listato 7-1, puoi recuperare overview dall'array usando la seguente linea di codice:
NSString *associatedObject = (NSString *)objc_getAssociatedObject(array, &overviewKey);

Rompere Associazioni

Per rompere un'associazione, si usa tipicamente objc_setAssociatedObject, passandogli nil come valore. Continuando l'esempio mostrato nel listato 7-1, potresti rompere l'associazione tra array e stringa overview usando la seguente linea di codice:
objc_setAssociatedObject(array, &overviewKey, nil, OBJC_ASSOCIATION_ASSIGN);
(Dato che l'oggetto associato è stato impostato a nil, la politica non è attualmente importante). Per rompere tutte le associazioni di un oggetto, puoi usare objc_removeAssociatedObjects. In generale, è sconsigliato usare questo dato che rompe tutte le associazioni per tutti i clienti. Usa questa funzione solo se hai bisogno di restaurare un oggetto a condizioni incontaminate.

Esempio Completo

Il seguente programma combina gli esempi di codice delle sezioni precedenti.
#import
#import

int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

static char overviewKey;

NSArray *array = [[NSArray alloc] initWithObjects:@"One", @"Two", @"Three", nil];
// For the purposes of illustration, use initWithFormat: to ensure we get a // deallocatable string
NSString *overview = [[NSString alloc] initWithFormat:@"%@", @"First three numbers"];

objc_setAssociatedObject(array, &overviewKey, overview, OBJC_ASSOCIATION_RETAIN);
[overview release];

NSString *associatedObject = (NSString *)objc_getAssociatedObject(array, &overviewKey);
NSLog(@"associatedObject: %@", associatedObject);

objc_setAssociatedObject(array, &overviewKey, nil, OBJC_ASSOCIATION_ASSIGN);
[array release];

[pool drain];
return 0;
}

Fine Ottava parte

Finisce qui l'ottavo articolo di questa Guida dedicato ai Riferimenti Associativi del linguaggio di programmazione Objective-C. Spero col tempo di realizzare un'utile manuale disponibile a tutti. Il prossimo post di questa guida, tratterà l'Enumerazione Veloce, un utile strumento che fornisce una sintassi concisa, efficiente e sicura, per enumerare i contenuti di una collezione. Segnalatemi eventuali errori, o commentate l'articolo se l'avete trovato utile, anche per incentivarmi a continuare a pubblicare le mie traduzioni. Iscrivetevi ai feed del blog per essere sempre aggiornati automaticamente ogni volta che sono disponibili nuovi contenuti. Nella barra laterale del blog potete trovare l'elenco di tutti gli articoli di questa guida.
Continua...

giovedì 29 aprile 2010

Guida Objective-C in Italiano - Parte 7 - Categorie ed Estensioni


Eccoci alla settima parte della Guida alla programmazione in Objective-C, oggi parleremo di Categorie ed Estensioni.

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 può 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.


Categorie ed Estensioni

Una categoria ti permette di aggiungere metodi ad una classe esitente - anche ad una di cui no hai il codice sorgente. Questa è una caratteristica molto potente che ti permette di estendere le funzionalità di una classe esistente senza crearne una sottoclasse. Usando le categorie, puoi anche dividere l'implementazione delle tue classi tra molti files. Le estensioni delle classi sono simili, ma permettono ad API aggiuntive richieste di essere dichiarate per una classe in locazioni diverse dal blocco @interface della classe primaria.

Aggiungere Metodi a Classi

Puoi aggiungere metodi ad una classe dichiarandoli in un file interfaccia sotto un nome di categoria e definendoli in un file implementazione sotto lo stesso nome. Il nome di categoria indica che i metodi sono delle aggiunte ad una classe dichiarata da qualche altra parte, non una nuova classe. non pouoi, comunque usare una categoria per aggiungere variabili di istanza ad una classe.

I metodi che la categoria aggiunge diventano parte del tipo della classe. Ad esempio i metodi aggiunti alla classe NSArray in una categoria sono tra i metodi che il compilatore si aspetta di trovare nel repertorio dei metodi di un'istanza della classe NSArray. I metodi aggiunti alla classe NSArray in una sottoclasse non sono inclusi nel tipo NSArray. (Questo importa solo per oggetti tipati staticamente dato che lo static typing è l'unico modo per il compilatore di conoscere la classe di un oggetto).

I metodi di Categoria possono fare qualunque cosa che possono fare i metodi definiti nella classe. A runtime non c'è differenza. I metodi che la categoria aggiunge alla classe sono ereditati da tutte le sottoclassi, proprio come gli altri metodi.

La dichiarazione di un'interfaccia di categoria appare molto come una dichiarazione di interfaccia di classe - tranne che il nome di categoria è elencato tra parentesi dopo il nome di classe e la superclasse non è menzionata. A meno che i suoi metodi non accedano ad alcuna variabile di istanza della classe, la categoria deve importare il file interfaccia della classe che estende:
#import "ClassName.h"



@interface ClassName ( CategoryName )

// method declarations

@end

L'implementazione, come al solito, importa la sua propria interfaccia. Una convenzione comune per i nomi è che il nome del file di base della categoria è il nome della classe che la categoria estende seguito da un "+" seguito dal nome della categoria. Un'implementazione di categoria (in un file nominato NomeClasse+NomeCategoria.m) potrebbe quindi apparire così:
#import "ClassName+CategoryName.h"



@implementation ClassName ( CategoryName )

// method definitions

@end

Nota che una categoria non può dichiarare variabil idi istanza aggiuntive per la classe; include solo metodi. Comnque, tutte le variabili di istanza entro lo scope della classe sono anche nello scope della categoria. questo include tutte le variabili di istanza dichiarate dalla classe, anche quelle dichiarate @private.

Non c'è limite al numero di categorie che puoi aggiungere ad una classe, ma ogni nome di categoria deve essere diverso, ed ognuno dovrebbe dichiarare e definire un insieme diverso di metodi.

Come Usare le Categorie

Ci sono tanti modi di usare le categorie:
  • Per estendere classi definite da altrei implementatori. Ad esempio, se puoi aggiungre metodi alle classi definite nel frameworks Cocoa. I metodi aggiunti sono ereditati dalle sottoclassi e sono indistinguibili a runtime dai metodi originali della classe.
  • Come alternativa ad una sottoclasse. Piuttosto che definire una sottoclasse per estendere una classe esistente, attraverso una categoria puoi aggiungeremetodi alla classe direttamente. Ad esempio, puoi aggiungere categorie a NSArray e altre classi Cocoa. Come nel caso di una sottoclasse, non hai bisogno del codice sorgente della classe che stai estendendo.
  • Per distribuire l'implementazione di una nuova classe in files sorgenti separati. Ad esempio, puoi raggruppare i metodi di una classe grande in diverse categorie e mettere ogni categoria in un file diverso. Quando sono usate in questo modo, le categorie possono beneficiare del processo di sviluppo in un vari modi - Esse:
    • Forniscono un semplice modo di raggruppare metodi correlati. Metodi simili definiti in classi diverse possono essere messi insieme nello stesso file sorgente.
    • Semplifica la gestione di una classe grande quando molti sviluppatori contribuiscono alla sua definizione.
    • Ti permette di raggiungere alcuni dei benefici della compilazione incrementale per una classe molto grande.
    • Può aiutare a migliorare la località delle referenze per metodi comunemente usati.
    • Ti Abilita a configurare una classe diversamente per applicazioni separate, senza dover mantenere diverse versioni dello stesso codice.

Sebbene il linguaggio ti permette di usare una categoria per sovrascrivere i metodi che la classe eredita, o anche i metodi dichiarati nell'interfaccia della classe, è fortemente sconsigliato l'uso di questa funzionalità. Una categoria non è una sostituta per una sottoclasse. Ci sono molte imperfezioni significanti:
  • Quando una categoria sovrascrive un metodo ereditato, il metodo nella categoria può, come è solito, invocare l'implementazione ereditata tramite un messaggio a super. Comunque, se una categoria sovrascrive un metodo che esiste già nella classe della categoria, non c'è modo per invocare l'implementazione originale.
  • Una categoria non può sovrascrivere con affidabilità i metodi dichiarati in un altra categoria della stessa classe. Questo problema è particolarmente significante dato che molte delle classi Cocoa sono implementate usando le categorie. Un metodo basato su framework che provi a sovrascrivere potrebbe essere stato implementato in una categoria, e quindi non è definito quale implementazione ha la precedenza.
  • La presena di alcuni metodi potrebbe causare cambi dei comportamenti tra tutti i frameworks. Ad esempio, se aggiungi un'implementazione di windowWillClose: a NSObject, questo causerà che tutte le finestre delegate risponderanno a quel metodo e potrebbero modificare il comportamento di tutte le istanze di NSWindow. Questo potrebbe causare cambiamenti misteriosi nel comportamento e potrebbe condurre a crashes.

Categorie della Classe Radice

Una categoria può aggiungere metodi ad ogni classe, incluso nella classe radice. I metodi aggiunti a NSObject diventano disponibili a tutte le classi che sono collegate al tuo codice. Mentre questo può essere utile a volte, può anche essere abbastanza pericoloso. Sebbene potrebbe sembrare che le modifiche che le categorie fanno siano ben capite e di impatto limitato, l'ereditarietà gli da un ampio scope. Potresti fare dei cambi non intenzionali a classi non viste; potresti non conoscere tutte le conseguenze di ciò che stai facendo. Inoltre, gli altri che non sono a conoscenza dei tuoi cambi non capiranno cosa stanno facendo.

In aggiunta, ci sono altre due considerazioni da tenere a mente quando si implementano metodi per la classe radice:
  • Messaggi a super sono invalidi (non c'è la superclasse).
  • Gli oggetti di classe possono eseguire metodi di istanza definiti nella classe radice.
Normalmente gli oggetti di classe possono eseguire solo metodi di classe. Ma i metodi di istanza definiti nella classe radice sono un caso speciale. Essi definiscono un'interfaccia al sistema di runtime che tutti gli oggetti ereditano. Gli oggetti di classe sono oggetti full-fledged e devono condividere la stessa interfaccia.

Questa caratteristica significa che hai bisogno di tener conto della possibilità che un metodo di istanza che definisci in una categoria della classe NSObject potrebbe essere eseguito non solo da istanze ma da anche da oggetti di classe. Ad esempio, nel corpo del metodo, self potrebbe significare un oggetto di classe così come un'istanza.

Estensioni

Le estensioni di classi sono come categorie "anonime", tranne che i metodi che dichiarano devono essere implementati nel blocco principale @implementation per la classe corrispondente.

È comune per una classe avere un'API pubblicamente dichiarata e poi avere API aggiuntiva dichiarata privatamente per solo uso della classe o del framework dentro il quale risiede la classe. Puoi dichiarare tale API in una categoria (o in più di una categoria) in un file header privato o in un file di implementazione come descritto sopra. Questo funziona, ma il compilatore non può verificare che tutti i metodi dichiarati siano implementati.

Ad esempio, il compilatore compilerà senza errori le seguenti dichiarazioni e implementazioni:
@interface MyObject : NSObject

{

NSNumber *number;

}

- (NSNumber *)number;

@end



@interface MyObject (Setter)

- (void)setNumber:(NSNumber *)newNumber;

@end





@implementation MyObject



- (NSNumber *)number {

return number;

}

@end

Nota che non c'è implementazione del metodo setNumber:. Se è invocato a runtime genererà un errore.

Le estensioni di classe ti permettono di dichiarare API aggiuntive richieste per una classe in locazioni diverse da dentro al blocco primario @interface della classe, come illustrato nel seguente esempio:
@interface MyObject : NSObject

{

NSNumber *number;

}

- (NSNumber *)number;

@end



@interface MyObject ()

- (void)setNumber:(NSNumber *)newNumber;

@end





@implementation MyObject



- (NSNumber *)number {

return number;

}

- (void)setNumber:(NSNumber *)newNumber {

number = newNumber;

}

@end

Nota che in questo caso:
  • Non è dato un nome nelle parentesi nel secondo blocco @interface;
  • L'implementazione del metodo setNumber: appare dentro al blocco @implementation princpiale della classe.
L'implementazione del metodo setNumber. deve apparire dentro al blocco @implementation princpale della classe (non poui implementarlo in una categoria). Se questo non è il caso, il compilatore emetterà un warning avvisando che non può trovare una definizione di metodo per setNumber:.

Fine Settima Parte

Finisce qui il settimo articolo di questa Guida dedicato alle Categorie e Estensioni del linguaggio di programmazione Objective-C. Spero col tempo di realizzare un'utile manuale disponibile a tutti. Il prossimo post di questa guida, tratterà i Riferimenti Associativi, un utile strumento per simulare l'aggiunta di variabili di istanza di un oggetto ad una classe esistente. Segnalatemi eventuali errori, o commentate l'articolo se l'avete trovato utile, anche per incentivarmi a continuare a pubblicare le mie traduzioni. Iscrivetevi ai feed del blog per essere sempre aggiornati automaticamente ogni volta che sono disponibili nuovi contenuti. Nella barra laterale del blog potete trovare l'elenco di tutti gli articoli di questa guida.
Continua...
Related Posts with Thumbnails