Ciao a tutti. Oggi voglio parlarvi di un aspetto molto importante nella programmazione ad oggetti e che ha un’inestimabile valore anche nella programmazione per iOS: la comunicazione tra oggetti.
Quando si ha a che fare con oggetti di varia natura, ci può capitare di voler far comunicare alcuni di essi per ottenere specifiche funzionalità. Solitamente, saremo noi stessi a creare una serie di classi che secondo la nostra architettura sono specificatamente progettati per comunicare tra loro. A questo proposito l’Objective-C ci mette a disposizione vari strumenti, ma due di essi sono i più comuni in tutti i progetti e negli stessi SDK forniti da Apple per la programmazione per iOS e Mac OS: notifiche e delegati.
Inviare e Ricevere Notifiche
Un modo molto semplice ed efficace per mettere in relazione due o più oggetti senza che essi debbano conoscere alcun che dell’altro o degli altri, è la classe NSNotificationCenter, che ci permette di far inviare e ricevere messaggi da e verso il centro notifiche di questa classe (che è un Singleton), ed eseguire azioni di conseguenza.
Il concetto fondamentale è che la classe NSNotificationCenter si pone come intermediario tra i nostri oggetti per favorirne la comunicazione. Il NotificationCenter raccoglie le notifiche che gli oggetti inviano e le invia agli oggetti che si sono registrati per riceverle. Un’oggetto, infatti può registrarsi presso il centro notifiche per ricevere una o più notifiche. Inoltre più oggetti possono registrarsi per ricevere la stessa notifica, il che rende ancora più utile questo strumento. Quando un’oggetto si registra per ricevere una notifica, può decidere di far effettuare ad un certo target un’azione (invocando un metodo del target, quindi).
Per registrare un oggetto al ricevimento di una notifica basta una linea di codice:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(unMetodo) name:@"Nome della notifica" object:nil];
Con questo comando, otteniamo l’oggetto Singleton “defaultCenter”, quindi registriamo l’oggetto come osservatore di notifiche per la notifica il cui nome indichiamo nel parametro “name” (ma possiamo registrare come osservatore anche un oggetto che self contiene, ad esempio una sua variabile d’istanza) e indichiamo il metodo che va invocato quando l’osservatore riceve la notifica. Nel parametro “object” possiamo specificare un oggetto come mittente della notifica. In pratica, se una stessa notifica può essere inviata da più oggetti, possiamo decidere che un’osservatore effettui l’azione prevista solo se il mittente della notifica è un determinato oggetto.
Registrare un oggetto come osservatore per fargli ricevere notifiche è dunque un processo molto molto semplice. Altrettanto semplice è inviare una notifica al centro notifiche:
[[NSNotificationCenter defaultCenter] postNotificationName:@"Nome della Notifica" object:nil userInfo:nil];
Ancora una volta otteniamo l’oggetto Singleton “defaultCenter” e pubblichiamo una notifica con un dato nome. Possiamo aggiungere poi variabili addizionali, object (cioè il mittente della notifica) e userInfo (un dizionario con le informazione dell’utente).
NOTA: è consigliabile usare delle costanti come nomi per le notifiche in modo tale da evitare errori di scrittura nel codice e rendere più agevole la modifica del nome della notifica in un secondo momento. Infatti, se volessimo cambiare il nome della notifica, in questo modo lo dovremmo fare solo una volta, nel file dove la costante è stata dichiarata, mentre quando andiamo a scrivere il nome della costante come parametro per i metodi usati dal notificationCenter, non trattandosi di una stringa, il compilatore ci avvertirà della presenza di eventuali errori di battitura.
Delegare non è un male!
L’uso delle notifiche rende molto più semplice la vita di noi poveri sviluppatori. D’altro canto, però, inviare notifiche può rivelarsi insufficiente per i nostri scopi. Ci ritroveremo allora nella condizione in cui delegare alcune azioni ad altri oggetti sia una pratica migliore. Questo accade specialmente quando vogliamo far si che una classe implementi dei metodi che permettano l’uso di un componente che abbiamo creato. In particolare, questa pratica si rivela utilissima quando chi userà il nostro componente non siamo noi, o quando il nostro componente può essere usato in tante classi diverse e a scopi diversi. Un’esempio di classe che è solita delegare ad altri aggetti alcune azioni è la classe UITableViewController, che delega alle sue sottoclassi il compito di fornire una fonte per le informazioni da mostrare nelle celle e di definire alcuni comportamenti delle celle (ad esempio, definire cosa succede quando una data cella viene selezionata).
È pratica comune definire i metodi da delegare nell’intestazione della classe che richiama il suo delegato per eseguire una data azione. Delegare dei metodi da implementare ad un’altro oggetto, implica anche definire quali sono questi metodi. Per farlo viene usata la parola chiava @protocol. Se ad esempio abbiamo una classe Box (Scatola) che vuole delegare al suo utente un metodo per definire cosa c’è nella scatola, la sua dichiarazione e implementazione saranno qualcosa del tipo:
@protocol BoxDelegate; // dichiara che esiste un protocollo chiamato BoxDelegate
@interface Box : NSObject
{
id delegate_; // scrivere tra parentesi angolari il nome di un protocollo subito dopo il tipo di una variabile, significa dichiarare che quella variabile deve conformarsi al dato protocollo (cioè adottarne i metodi)
}
@property (nonatomic, retain) id delegate;
- (void)riempiScatola;
@end
@protocol BoxDelegate // definiamo i metodi che compongono il protocollo
- (NSArray *)inserisciOggetti;
@optional
- (void)svuotaScatola;
@end
Nell’interfaccia di questa classe definiamo il protocollo con due metodi. I metodi che indichiamo nel protocollo, normalmente sono da implementare obbligatoriamente all’interno del delegato, ma possiamo decidere di rendere l’implementazione di alcuni di essi facoltativa dichiarandoli dopo la parola chiave @optional. La variabile delegate che abbiamo definito, deve conformarsi al protocollo, quindi implementarne i metodi obbligatori.
Una possibile implementazione del metodo d’istanza della classe Box “riempiScatola”, potrebbe essere:
- (void)riempiScatola
{
NSArray *oggetti = [NSArray arrayWithArray:[self.delegate oggettiNellaScatola]];
// riempi la scatola
}
Per implementare i metodi di un protocollo in una classe, dobbiamo dichiarare che la classe si conforma al dato protocollo. Per far ciò ci basta scrivere nell’intestazione della classe, subito dopo il nome della sua superClasse, il nome del protocollo fra parentesi angolari “
@interface MyBox : UIViewController
Questa classe ovviamente dovrà dichiararsi come delegato per un protocollo anche con un’istruzione del tipo:
Box *laMiaScatola = [[Box alloc] init];
laMiaScatola.delegate = self;
e ricordatevi di implementare opportunamente i metodi dichiarati nel protocollo come obbligatori!
Conclusioni
Mettere in comunicazione più oggetti senza far interagire troppo una classe con un’altra non è un compito semplice ma per fortuna i design patterns di delegazione e notifica ci vengono in aiuto. Questi non sono gli unici metodi per far comunicare più oggetti fra loro ma sono i più semplici e utili. Per un’ancora più efficace comunicazione tra due o più oggetti potreste utilizzare tranquillamente entrambi i metodi, delegando azioni e notificando eventi che ne inneschino altre.
Che dire di più? Divertitevi a far comunicare tra loro i vostri oggetti! 🙂
8 Responses to “L#019 – Comunicazione tra oggetti: Notifiche e Delegati in Objective-C”
25 Luglio 2011
Giovambattista FazioliPer dettagli:
http://www.undolog.com/2011/03/10/objective-c-notifiche-e-delegati/
25 Luglio 2011
Andrea Cappellottose devo essere sincero questo articolo non mi piace per niente…. preferisco molto di più quello linciato da Giovambattista….
25 Luglio 2011
Ignaziocche sei aspro! 🙂 diciamo che forse non è chiarissimo per una persona alle prime armi, su questo ti do ragione..
25 Luglio 2011
Andrea Cappellottoil correttore di Lion mi ha preso alla lettera..:) linciato al posto di linkato..:) e poi era da un mese che appena riproponevo che appena finite le app urgenti facevo un tut sui delegati…;)
2 Agosto 2011
GiuseppeConcordo con i commenti precedenti. Apprezzo moltissimo il lavoro che state facendo e continuerò a seguirvi, ma questo capitolo è stato poco chiaro. Sarebbe meglio inserire alcuni esempi costruiti passo passo.
4 Agosto 2011
ultrakorneè un argomento molto importante, e i delegate se uno non è pratico possono essere ostici, hanno bisogno di un analisi più approfondita.
Consiglierei di trattarli insieme all MVC così da far capire bene il bisogno del delegate, e la differenza con le notifications (e magare citare il KVO).
poi un errore:
@property (nonatomic, retain) id delegate;
bisognerebbe tenere un weak reference al delegate (quindi assign non retain). facendo retain si rischia un retain ciclico dove 2 oggetti si trattengono a vicenda.
17 Giugno 2012
Objective C Delegate | CianiAndreaDev[…] http://www.devapp.it/wordpress/l019-comunicazione-tra-oggetti-notifiche-e-delegati-in-objective-c.ht… […]
16 Giugno 2013
AndreaUn altro tutorial dettagliato lo potete trovare QUI
http://www.cianiandrea.it/archives/413