Anche nei tutorial più semplici è possibile incontrare l’uso dei protocolli. Sarà certamente capitato a molti di utilizzare nel vostro view controller un protocollo, inserendo, accanto alla definzione dell’interfaccia, una dicitura simile a:
1 2 3 | @interface myViewController : UIViewController <UIWebViewDelegate> { ... } |
I protocolli (nell’esempio sopra <UIWebViewDelegate>) sono un particolare modo di Objective-C di far ereditare comportamenti di una classe in un’altra. Nell’esempio mostrato sopra il nostro view controller probabilmente gestisce un componente UIWebView. Volendo rispondere agli eventi di quest’ultimo ne incorpora il protocollo UIWebViewDelegate.
Così, implementato il protocollo UIWebViewDelegate nel nostro view controller, possiamo inserire nel file .m di implementazione i metodi (messaggi) che saranno invocati dal componente UIWebView, come ad esempio:
1 2 3 | - (void)webViewDidFinishLoad:(UIWebView *)webView { // Caricamento pagina terminato } |
In altre parole il nostro view controller condivide l’interfaccia <UIWebViewDelegate>, cioè ne eredita “alcune” caratteristice.
Scrivere un nostro protocollo
Immaginiamo di avere due classi: ClasseA e ClasseB. La classe ClasseA istanzia al suo interno la ClasseB che, a sua volta, genera un messaggio quando accade un determinato evento. Vorremo quindi scrivere un delegato per la ClasseB tale che possa essere utilizzato da ClasseA per rispondere e intercettare gli “eventi” rilasciati da ClasseB.
La prima cosa da fare è aggiungere la definizione del protocollo nella ClasseB insieme ad una property (nello standard Apple) delegate:
1 2 3 4 5 6 7 8 9 10 11 | @protocol ClasseBDelegate <NSObject> @optional - (void)mioEvento:(id)sender; @end @interface ClasseB : NSObject { // altre definizioni id <ClasseBDelegate> delegate; } @property (assign) id <ClasseBDelegate> delegate; |
Come da prassi, nel file .m della ClasseB inserire:
1 | @synthesize delegate; |
Nel file di implementazione della classe ClasseB possiamo inserire il “fire” del nostro evento in questo modo:
1 2 3 | if (self.delegate != NULL && [self.delegate respondsToSelector:@selector(mioEvento:)]) { [delegate mioEvento:self]; } |
Prima di tutto verifichiamo che sia stato impostato un delegato: self.delegate != NULL e che questo delegato fornisca, o possa rispondere, al messaggio mioEvento: [self.delegate respondsToSelector:@selector(mioEvento:)]. Se entrambi le condizioni sono verificate allora viene invocato mioEvento; in questo esempio questo evento prevede anche un parametro (id) che è il puntatore all’istanza della classe ClassB.
La classe ClassA non dovrà far altro che implementare il delegato e inserire il metodo mioEvento:
1 2 3 4 5 6 7 | #import "ClasseB.h" @interface ClasseA : NSObject <ClasseBDelegate> { ... } - (void)mioEvento:(id)sender; |
Nel file di implementazione quando creiamo la ClassB useremo:
1 2 | ClasseB *classeB = [ClasseB alloc]; [classeB setDelegate: self]; |
Poi, inseriremo:
1 2 3 | - (void)mioEvento:(id)sender { NSLog(@"mioEvento"); } |









7
Ciao,
complimenti dell’articolo!
Volevo chiarire un mio dubbi sull’utilizzo…
Il delegato viene solitamente richiamato quando accade un evento…in questo caso avendo creato noi il “delegato” il metodo “mioEvento” dovrà essere invocato a mano?
Non ho capito questo passaggio
Grazie.
@Luigi: si, la classe che supporta il protocollo da noi creata avrà anche il compito di invocare il metodo delegato. Come mostrato sopra nella nostra classe all’accadere dell’evento si eseguirà un codice simile a:
2
3
[delegate mioEvento:self];
}
Ovvero, se è stato impostato un delegato e tale delegato supporto il metodo richiesto, allora il metodo viene eseguito.
In linea di massima classi pesonali, di media complessità, dovranno – è il caso – supportatre un loro protocollo per informare su stati ed eventi della classe stessa.
Se altri dubbi scrivi pure.
Grazie…
ti faccio un altra domanda…nasce dal fatto che non ho ancora messo mano al codice e magari sparo sciocchezze
Creo il mio protocollo e mettiamo caso che all’accadere di quell’evento lo richiamo un dato metodo del protocollo come tu hai indicato.
L’implementazione del metodo la farò ovviamente nel .m del mio protocollo
Se devo richiamare un metodo che fa parte della mia classe delegante ( cio quella che sfrutta il delegato ) come procederò?
Ho una mia idea ma non voglio scrivere bagianate
@Luigi: se ho capito bene la tua domanda, normalmente ci si trova di fronte a due casi. Di solito si ha un view controller che oltre ad istanziare l’oggetto che supporta un dato protocollo ne fa anche da delegato. In questo caso quando si scrive l’implementazione del metodo sia ha diretto accesso all’istanza dell’oggetto che fornisce il protocollo. Questo può essere schematizzato nel modo seguente:
2
3
myClassA *objectA;
}
myViewControllerinstanzierà l’oggetto del tipomyClassAe, come detto sopra, se funzngerà anche da delegato per lamyClassAnell’implementazione del metodo avrà accesso alla variabileobjectA:2
3
4
// do something
[objectA mioMetodo];
}
In laternativa ci sono casi in cui il delegato di un oggetto è una classe diversa, del tipo:
2
3
4
5
6
...
objectA = [[myClassA alloc] init];
myClassB *objectB = [[myClassB alloc] init];
objectA.delegate = objectB;
...
In questo caso un view controller istanzia la classe
myClassAindicando a questa come delegato unamyClassB. L’evento generato damyClassAverrà gestito damyClassBche, ovviamente, non conosce a priopri il puntatore allamyClassA.In questo caso se l’istanza di
myClassBdeve interagire con l’istanza dimyClassAqualcuno deve passargliela; questo qualcuno è il view controller che conosce entrambe. Quindi il codice di sopra dovrebbe essere del tipo:2
3
4
5
6
7
...
objectA = [[myClassA alloc] init];
myClassB *objectB = [[myClassB alloc] init];
[objectB setObjectAPointer: objectA]; // informa B di A
objectA.delegate = objectB;
...
Se ho capito bene la domanda…
nel primo caso…
io chiamo il metodo mioMetodo che avrà ovviamente una sua implementazione.
Bene…in quella implementazione facciamo conto di eseguire un’azione che prevede un’invocazione di un metodo o l’utilizzo di una variabile presente nel mio myViewController
Come faccio?
Spero di essermi spiegato!
[...] elegante, chiaro e semplice da usare per affrontare queste situazioni. Ne avevamo già parlato in Come creare un proprio protocollo con delegato. Objective-C permette di definire un protocollo di comunicazione tramite il quale una o più classi [...]
Ciao, complimenti anche da parte mia per l’articolo.. ho una perplessità da esporti..
mi trovo nel caso in cui sono in un tab controller, ogni viewController ha una classe dedicata
tab1: PrimoController
tab2: OptionController
quello che voglio fare io è poter stoppare e startare un timer che sta sul PrimoController dall’OptionController, questo timer lo posso anche startare e stoppare dal primo controller con 2 metodi
2
3
4
5
6
// NSTimer bla bla bla
}
- (void)stopTimer{
// timer invalidate bla bla bla
}
Io ho fatto così:
in
OptionViewController.hho creato2
3
4
5
6
7
8
9
10
@required
- (void)timerActionRestart;
@end
@interface OptionViewController : UITableController
@property (weak, nonatomic) id timerHandlerDelegate;
@end
in
OptionViewController.mho:2
3
4
5
6
7
8
{
if (timerHandlerDelegate != nil){
NSLog(@"timerActionRestar");
[self.timerHandlerDelegate timerActionRestart];
}
}
E fin qui tutto ok.. Andando invece nella prima classe la situazione è la seguetne:
2
3
4
5
6
7
8
9
10
11
12
13
#import "OptionViewController.h"
@interface OptionViewController : NSObject
/** PrimaClasse.m **/
- (void)timerActionRestart
{
// Timer stop
// Timer start
NSLog("timer restarting");
}
Ho capito che devo settare il delegato ma essendo 2 classi già inizializzate, non so come fare!
Hai idea?
Ora come ora noto che
metodoGestoreTimerviene richiamato ma non logga nulla in quanto fallisce il controllo sull’if..Grazie mille e ancora complimenti per il blog!
dimenticato.. in
OptionViewControlleril@syntetizedel delegato l’ho messoScusa lo spam.. ho notato che c’è un errore..
ecco la correzione
2
3
4
#import "OptionViewController.h"
@interface PrimaClasse: NSObject
@Marco: Ti consiglio un approccio credo più corretto. Se hai eseguito il subclass del tab controller, quest’ultimo detiene la lista di tutti i view controller. La questione dev’essere letta in questi termini: il view controller option chiede ad un altro controller di eseguire un’operazione (attivare o disattiva un timer). Il view controller option è “figlio”, come gli altri, del tab controller. È dunque più corretto che il messaggio vada dal view controller option verso il tab controller che a sua volta smisterà il messaggio a chi di dovere. Il tab view controller conosce tutti i suoi view controller, quindi può inviare un messaggio a chiunque. Il view controller option conosce il suo tab controller per definizione.
Se ho tempo cercherò di portare un esempio da codice ma penso di averti dato tutte le info necessarie anche per procedere da solo. Fammi sapere se mi sono perso qualcosa per strada…
Ti ringrazio moltissimo, mi hai illuminato
ho risolto impostando
2
3
4
5
6
7
- (void)viewDidLoad{
....
//setto il delegato per il restart del timer
NSArray *viewControllers = self.tabBarController.viewControllers;
self.timerHandlerDelegate = [viewControllers objectAtIndex:0];
}
Ora funziona tutto!
grazie mille!
[...] http://www.undolog.com/2010/02/10/come-creare-un-proprio-protocollo-con-delegato/ [...]