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

Esercizi Strutture Dati - Queue e Deque

Ecco la seconda parte degli esercizi di Strutture Dati (SD) di Laboratorio di Algoritmi e Strutture Dati (LASD) con il professor Salvatore La Torre. Nel seguito di questo articolo ci saranno le strutture dati Queue (Coda) e Deque (Double ended Queue).






Esercizi su Queue

public interface Queue<E>
{
 /**
  * Restituisce il numero di elementi presenti nella coda.
  * @return un int da 0 alla lunghezza max della coda.
  */
 public int size();
 /**
  * Controlla se la coda è vuota.
  * @return true, se la coda è vuota, false altrimenti.
  */
 public boolean isEmpty();
 /**
  * Accede all'elemento sul front della coda.
  * @return l'elemento al front della coda.
  * @throws EmptyQueueException se invocato su una coda vuota.
  */
 public E front() throws EmptyQueueException;
 /**
  * Inserisce un elemento nel rear della coda.
  * @param element l'elemento da inserire.
  */
 public void enqueue (E element);
 /**
  * Rimuove l'elemento sul front della coda.
  * @return l'elemento rimosso.
  * @throws EmptyQueueException se invocato su una coda vuota.
  */
 public E dequeue()throws EmptyQueueException;
}

Implementare l’interfaccia Queue (scrivere la classe ArrayQueue) usando un array di lunghezza fissata 􏰁􏰁 public static final int CAPACITY = 1024;



􏰀 Implementare Queue in modo che la coda piena invece della FullQueueException causa un aumento della taglia della coda

public class ArrayQueue<E> implements Queue<E>
{
 public static final int CAPACITY = 1024;
 private int f,r,capacity,size;
 private E coda[];

 public ArrayQueue()
 {
  this(CAPACITY);
 }

 public ArrayQueue(int cap)
 {
  capacity = cap;
  f=r=size=0;
  coda = (E[])new Object[capacity];
 }

 @Override
 public int size() {
  return size;
 }

 @Override
 public boolean isEmpty() {
  return (f==r);
 }

 @Override
 public E front() throws EmptyQueueException 
 {
  if (isEmpty())
   throw new EmptyQueueException("Coda Vuota!");
 
  return coda[f];
 }

 @Override
 public void enqueue(E element) 
 {
  if (size == capacity-1)
  {
   E nuovaCoda[] = (E[])new Object[capacity*2];
   for(int i=0; i<size; i++)
   {
    nuovaCoda[i]=coda[(f+i)%capacity];
   }
   f=0;
   r=size;
   capacity *=2;
   coda = null;
   coda = nuovaCoda;
   nuovaCoda = null;
  }

  coda[r] = element;
  r = (r+1)%capacity;
  size++;
 }

 @Override
 public E dequeue() throws EmptyQueueException 
 {
  if (isEmpty())
   throw new EmptyQueueException("Coda Vuota!");
 
  E tmp = coda[f];
  coda[f] = null;
  f = (f+1)%capacity;
  size--;
  return tmp;
 }

 public String toString()
 {
  String response = "ArrayQueue Elements:[ ";
  for (int i = 0; i<size; i++)
  {
   response += ((i+1)+"°"+coda[(f+i)% capacity]+" ");
  }
  response+= "] size:"+size()+" capacity: "+capacity;
  return response;
 }

 public E extract(int k) throws  NotEnoughElements
 {
  if (size() < k+1)
   throw new NotEnoughElements("La Coda non contiene abbastanza elementi!");
 
  return coda[(f+k)%capacity];
 }
}

Scrivere un programma che testi la vostra implementazione di Queue invocandone tutti i metodi





public class ArrayQueueTest 
{
 public static void main(String[] args) 
 {
  ArrayQueue<String> coda = new ArrayQueue<String>(2);
  //QueueWithDeque<String> coda = new QueueWithDeque<String>();
  //LinkedQueue<String> coda = new LinkedQueue<String>();

  coda.enqueue("Marco"); 
  System.out.println(coda);

  coda.enqueue("Rosaria"); 
  System.out.println(coda);

  coda.enqueue("Francesco"); 
  System.out.println(coda);

  coda.enqueue("Pippo"); 
  System.out.println(coda);

  coda.dequeue();
  System.out.println(coda);
 
  coda.enqueue("Giuseppe");
  System.out.println(coda);
 
  System.out.println("Front: "+coda.front());
  //System.out.println("3° elem dal front: "+coda.extract(3));

  coda.dequeue();
  System.out.println(coda);
 
  coda.dequeue();
  System.out.println(coda);
 
  coda.dequeue();
  System.out.println(coda);
 
  coda.dequeue();
  System.out.println(coda);
 }
}


Esercizi su Deque

public interface Deque<E> 
{
 /**
  * Restituisce il numero di elementi contenuti.
  * @return un valore intero >= 0
  */
 public int size(); 

 /**
  * Verifica se il deque è vuoto.
  * @return true se è vuoto, false altrimenti.
  */
 public boolean isEmpty(); 

 /**
  * Restituisce il primo elemento del Deque.
  * @throws EmptyDequeException se invocato su Deque vuoto.
  */
 public E getFirst() throws EmptyDequeException;

 /**
  * Restituisce l'ultimo elemento del Deque.
  * @throws EmptyDequeException se invocato su Deque vuoto.
  */
 public E getLast() throws EmptyDequeException;

 /**
  * Aggiunge un nuovo elemento all'inizio del Deque.
  * @param element il nuovo elemento da inserire.
  */
 public void addFirst (E element);

 /**
  * Aggiunge un nuovo elemento alla fine del Deque.
  * @param element il nuovo elemento da inserire.
  */
 public void addLast (E element); 

 /**
  * Rimuove e restituisce il primo elemento del Deque.
  * @throws EmptyDequeException se invocato su Deque vuoto.
  */
 public E removeFirst() throws EmptyDequeException;

 /**
  * Rimuove e restituisce l'ultimo elemento del Deque.
  * @throws EmptyDequeException se invocato su Deque vuoto.
  */
 public E removeLast() throws EmptyDequeException; 
}


Per implementare il TDA Deque usiamo una lista doppiamente concatenata




public class DLNode<E>
{
 private E element;
 private DLNode<E> prev, next;

 public DLNode()
 {
  this(null,null,null);
 }
 public DLNode(E e, DLNode<E> p, DLNode<E> n)
 {
  element = e;
  prev = p;
  next = n;
 }

 //metodi accessori
 public E getElement()
 {
  return element;
 }
 public DLNode<E> getPrev()
 {
  return prev;
 }
 public DLNode<E> getNext()
 {
  return next;
 }

 //metodi modificatori
 public void setElement(E e)
 {
  element = e;
 }
 public void setPrev(DLNode<E> p)
 {
  prev = p;
 }
 public void setNext(DLNode<E> n)
 {
  next = n;
 }
}


Implementare il TDA Deque (class MyDeque) con lista doppiamente concatenata

public class MyDeque<E> implements Deque<E>
{

 private int size;
 private DLNode<E> header, trailer;

 public MyDeque()
 {
  header = new DLNode<E>();
  trailer = new DLNode<E>();
  header.setNext(trailer);
  trailer.setPrev(header);
  size = 0;
 }

 @Override
 public int size() 
 {
  return size;
 }

 @Override
 public boolean isEmpty() 
 {
  return (size == 0);
 }

 @Override
 public E getFirst() throws EmptyDequeException 
 {
  if (isEmpty())
   throw new EmptyDequeException("Deck Vuoto!");
 
  return header.getNext().getElement();
 }

 @Override
 public E getLast() throws EmptyDequeException 
 {
  if (isEmpty())
   throw new EmptyDequeException("Deck Vuoto!");
 
  return trailer.getPrev().getElement();
 }

 @Override
 public void addFirst(E element) 
 {
  DLNode<E> newNode = new DLNode<E>(element,header,header.getNext());
  header.getNext().setPrev(newNode);
  header.setNext(newNode);
  size++;
 }

 @Override
 public void addLast(E element) 
 {
  DLNode<E> newNode = new DLNode<E>(element,trailer.getPrev(),trailer);
  trailer.getPrev().setNext(newNode);
  trailer.setPrev(newNode);
  size++;
 }

 @Override
 public E removeFirst() throws EmptyDequeException 
 {
  if (isEmpty())
   throw new EmptyDequeException("Deck Vuoto! - Rimozione primo elem Impossibile");

  E tmp = header.getNext().getElement();
  header.setNext(header.getNext().getNext());
  header.getNext().setPrev(header);
  size--;
  return tmp;
 }

 @Override
 public E removeLast() throws EmptyDequeException 
 {
  if (isEmpty())
   throw new EmptyDequeException("Deck Vuoto! - Rimozione ultimo elem Impossibile");

  E tmp = trailer.getPrev().getElement();
  trailer.setPrev(trailer.getPrev().getPrev());
  trailer.getPrev().setNext(trailer);
  size--;
  return tmp;
 }

 public String toString()
 {
  String risp = "Deque Elements: [ ";
 
  DLNode<E> nodo = header.getNext();
  for (int i=0; i<size; i++ )
  {
   risp+= (i+1)+"°"+nodo.getElement() + " ";
   nodo = nodo.getNext();
  }
  risp+= "] size: "+size;
 
  return risp;
 }
}



Usare Deque per implementare Stack e Queue


public class StackWithDeque<E> implements Stack<E>
{

 private MyDeque<E> contenitore;

 public StackWithDeque()
 {
  contenitore = new MyDeque<E>();
 }

 @Override
 public boolean isEmpty() 
 {
  return contenitore.isEmpty();
 }

 @Override
 public E top() throws EmptyStackException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.getLast();
  }
  catch (EmptyDequeException e)
  {
   throw new EmptyStackException();
  }
  return tmp;
 }

 @Override
 public E pop() throws EmptyStackException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.removeLast();
  }
  catch (EmptyDequeException e)
  {
   throw new EmptyStackException();
  }
  return tmp;
 }

 @Override
 public void push(E o) throws FullStackException 
 {
  contenitore.addLast(o);
 }

 @Override
 public int size() 
 {
  return contenitore.size();
 }

 public String toString()
 {
  return(contenitore.toString());
 }

}

public class QueueWithDeque<E> implements Queue<E> 
{
 private MyDeque<E> contenitore;

 public QueueWithDeque()
 {
  contenitore = new MyDeque<E>();
 }

 @Override
 public int size() 
 {
  return contenitore.size();
 }

 @Override
 public boolean isEmpty() 
 {
  return contenitore.isEmpty();
 }

 @Override
 public E front() throws EmptyQueueException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.getFirst();
  }
  catch(EmptyDequeException e)
  {
   throw new EmptyQueueException();
  }
  return tmp;
 }

 @Override
 public void enqueue(E element) 
 {
  contenitore.addLast(element);
 }

 @Override
 public E dequeue() throws EmptyQueueException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.removeFirst();
  }
  catch(EmptyDequeException e)
  {
   throw new EmptyQueueException();
  }
  return tmp;
 }

 public String toString()
 {
  return "Queue with "+contenitore.toString();
 }
}




Implementareilconcettodilistaconcatenatacome classe Java LinkedList



public class Node<E> 
{
 private E element;
 private Node<E> next;

 public Node()
 {
  this(null,null);
 }
 public Node(E e, Node<E> n)
 {
  element = e;
  next = n;
 }

 public E getElement()
 {
  return element;
 }
 public Node<E> getNext()
 {
  return next;
 }

 public void setElement(E e)
 {
  element = e;
 }
 public void setNext(Node<E> n)
 {
  next = n;
 }
}

public class LinkedList<E>
{
 private Node<E> head, tail;
 private int size;


 public LinkedList()
 {
  tail = new Node<E>();
  head = tail;
  size = 0;
 }
 /**
  * Restituisce il numero di elementi contenuti nella lista.
  * @return un valore intero >= 0
  */
 public int size()
 {
  return size;
 }
 /**
  * Verifica se la lista è vuota.
  * @return true se è vuota, false altrimenti.
  */
 public boolean isEmpty()
 {
  return (size == 0);
 }

 /**
  * Aggiunge un nuovo elemento all'inizio della lista.
  * @param e il nuovo elemento da inserire.
  */
 public void addFirst(E e)
 {
  if (size == 0)
   head.setElement(e);
  else
  {
   Node<E> newNode = new Node<E>(e,head);
   head = newNode;  
  }
  size++;
 }
 /**
  * Aggiunge un nuovo elemento alla fine della lista.
  * @param e il nuovo elemento da inserire.
  */
 public void addLast(E e)
 {
  if (size == 0)
   tail.setElement(e);
  else
  {
   Node<E> newNode = new Node<E>(e,null);
   tail.setNext(newNode);
   tail = newNode;  
  }
  size++;
 }

 /**
  * Rimuove e restituisce il primo elemento dalla lista.
  * @throws EmptyLinkedListException se invocato sulla lista vuota.
  */
 public E removeFirst() throws EmptyLinkedListException
 {
  if (isEmpty())
   throw new EmptyLinkedListException("Lista Vuota!");
 
  E tmp = null;
  if (size == 1)
  {
   tmp = head.getElement();
   head.setElement(null);
  }
  else
  {
   tmp = head.getElement();
   head = head.getNext();
  }
  size--;
  return tmp;
 }

 /**
  * Rimuove e restituisce l'ultimo elemento della lista.
  * @throws EmptyLinkedListException se invocato su lista vuota.
  */
 public E removeLast() throws EmptyLinkedListException
 {
  if (isEmpty())
   throw new EmptyLinkedListException();

  E tmp = null;
  if(size == 1)
  {
   tmp = tail.getElement();
   tail.setElement(null);
   size--;
  }
  else
  {
   Node<E> nodo = head;
   for(int i=0; i<size-1; i++)
   {
    if (nodo.getNext().getNext() == null)
    {
     tmp = nodo.getNext().getElement();
     nodo.setNext(null);
     tail = nodo;
     size--;
    }
    else
    {
     nodo = nodo.getNext();
    }
   } 
  }
 
  return tmp;
 }

 /**
  * Restituisce il primo elemento della lista.
  * @throws EmptyLinkedListException se invocato su lista vuota.
  */
 public E getFirst() throws EmptyLinkedListException
 {
  if (isEmpty())
   throw new EmptyLinkedListException();

  return head.getElement();
 }
 /**
  * Restituisce l'ultimo elemento della lista.
  * @throws EmptyLinkedListException se invocato su lista vuota.
  */
 public E getLast() throws EmptyLinkedListException
 {
  if (isEmpty())
   throw new EmptyLinkedListException("Lista Vuota!");

  return tail.getElement();
 }

 public String toString()
 {
  String stringa = "LinkedList Elements: [ ";
  Node<E> nodo = head;
  for(int i=0; i<size; i++)
  {
   stringa += nodo.getElement()+" ";
   nodo = nodo.getNext();
  }
  stringa += "] size: "+size;
  return stringa;
 } 
}



ImplementareilTDAStackconlistaconcatenata 
􏰁 Definire classe LinkedStack


public class LinkedStack<E> implements Stack<E>
{
 private LinkedList<E> contenitore;

 public LinkedStack()
 {
  contenitore = new LinkedList<E>();
 }

 @Override
 public boolean isEmpty() 
 {
  return contenitore.isEmpty();
 }

 @Override
 public E top() throws EmptyStackException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.getLast();
  }
  catch (EmptyLinkedListException e)
  {
   throw new EmptyStackException();
  }
  return tmp;
 }

 @Override
 public E pop() throws EmptyStackException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.removeLast();
  }
  catch (EmptyLinkedListException e)
  {
   throw new EmptyStackException();
  }
  return tmp;
 }

 @Override
 public void push(E o) throws FullStackException 
 {
  contenitore.addLast(o);
 }

 @Override
 public int size() 
 {
  return contenitore.size();
 }

 public String toString()
 {
  return contenitore.toString();
 }

}


ImplementareilTDAQueueconlistaconcatenata 
􏰁 Definire classe LinkedQueue


public class LinkedQueue<E> implements Queue<E> 
{
 private LinkedList<E> contenitore;

 public LinkedQueue()
 {
  contenitore = new LinkedList<E>();
 }

 @Override
 public int size() {
  return contenitore.size();
 }

 @Override
 public boolean isEmpty() {
  return contenitore.isEmpty();
 }

 @Override
 public E front() throws EmptyQueueException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.getFirst();
  }
  catch(EmptyLinkedListException e)
  {
   throw new EmptyQueueException();
  }
  return tmp;
 }

 @Override
 public void enqueue(E element) {
  contenitore.addLast(element);
 }

 @Override
 public E dequeue() throws EmptyQueueException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.removeFirst();
  }
  catch(EmptyLinkedListException e)
  {
   throw new EmptyQueueException();
  }
  return tmp;
 }

 public String toString()
 {
  return "QUEUE with "+contenitore.toString();
 }

}



􏰀 ImplementareilTDADequeconlisteconcatenate



public class LinkedDeque<E> implements Deque<E>
{
 private LinkedList<E> lista;

 @Override
 public int size() {
  return lista.size();
 }

 @Override
 public boolean isEmpty() {
  return lista.isEmpty();
 }

 @Override
 public E getFirst() throws EmptyDequeException {
  E tmp = null;
  try
  {
   tmp = lista.getFirst();
  }
  catch (EmptyLinkedListException e)
  {
   throw new EmptyDequeException();
  }
  return tmp;
 }

 @Override
 public E getLast() throws EmptyDequeException {
  E tmp = null;
  try
  {
   tmp = lista.getLast();
  }
  catch (EmptyLinkedListException e)
  {
   throw new EmptyDequeException();
  }
  return tmp; }

 @Override
 public void addFirst(E element) {
  lista.addFirst(element);
 }

 @Override
 public void addLast(E element) {
  lista.addLast(element);
 }

 @Override
 public E removeFirst() throws EmptyDequeException {
  E tmp = null;
  try
  {
   tmp = lista.removeFirst();
  }
  catch (EmptyLinkedListException e)
  {
   throw new EmptyDequeException();
  }
  return tmp;
 }

 @Override
 public E removeLast() throws EmptyDequeException 
 {
  E tmp = null;
  try
  {
   tmp = lista.removeLast();
  }
  catch (EmptyLinkedListException e)
  {
   throw new EmptyDequeException();
  }
  return tmp;  
 }
}






Continua...
Related Posts with Thumbnails