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.

Nessun commento:

Posta un commento

Related Posts with Thumbnails