Come creare un proprio protocollo con delegato

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");
}

12 commenti a: “ ”

  1. 06 gen, 2011 Luigi:

    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.

  2. 09 gen, 2011 Giovambattista Fazioli:

    @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:

    1
    2
    3
    if (self.delegate != NULL && [self.delegate respondsToSelector:@selector(mioEvento:)]) {
        [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.

  3. 09 gen, 2011 Luigi:

    Grazie…
    ti faccio un altra domanda…nasce dal fatto che non ho ancora messo mano al codice e magari sparo sciocchezze :D

    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

  4. 09 gen, 2011 Giovambattista Fazioli:

    @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:

    1
    2
    3
    @interface myViewController : UIViewController <myClassADelegate> {
      myClassA *objectA;
    }

    myViewController instanzierà l’oggetto del tipo myClassA e, come detto sopra, se funzngerà anche da delegato per la myClassA nell’implementazione del metodo avrà accesso alla variabile objectA:

    1
    2
    3
    4
    - (void)mioEvento {
        // do something
        [objectA mioMetodo];
    }

    In laternativa ci sono casi in cui il delegato di un oggetto è una classe diversa, del tipo:

    1
    2
    3
    4
    5
    6
    // ci troviamo in un view controller
    ...
         objectA = [[myClassA alloc] init];
         myClassB *objectB = [[myClassB alloc] init];
         objectA.delegate = objectB;
    ...

    In questo caso un view controller istanzia la classe myClassA indicando a questa come delegato una myClassB. L’evento generato da myClassA verrà gestito da myClassB che, ovviamente, non conosce a priopri il puntatore alla myClassA.
    In questo caso se l’istanza di myClassB deve interagire con l’istanza di myClassA qualcuno deve passargliela; questo qualcuno è il view controller che conosce entrambe. Quindi il codice di sopra dovrebbe essere del tipo:

    1
    2
    3
    4
    5
    6
    7
    // ci troviamo in un view controller
    ...
         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…

  5. 10 gen, 2011 Luigi:

    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!

  6. 10 mar, 2011 Objective-C: notifiche e delegati | Undolog.com:

    [...] 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 [...]

  7. 02 feb, 2012 Marco:

    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

    1
    2
    3
    4
    5
    6
    - (void)startTimer{
    // NSTimer bla bla bla
    }
    - (void)stopTimer{
    // timer invalidate bla bla bla
    }

    Io ho fatto così:
    in OptionViewController.h ho creato

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @protocol OptionViewControllerDelegate
    @required
    - (void)timerActionRestart;
    @end

    @interface OptionViewController : UITableController

    @property (weak, nonatomic) id  timerHandlerDelegate;

    @end

    in OptionViewController.m ho:

    1
    2
    3
    4
    5
    6
    7
    8
    - (void) metodoGestoreTimer
    {
     if (timerHandlerDelegate != nil){
       NSLog(@"timerActionRestar");
       [self.timerHandlerDelegate timerActionRestart];
     }

    }

    E fin qui tutto ok.. Andando invece nella prima classe la situazione è la seguetne:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /** PrimaClasse.h **/

    #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 metodoGestoreTimer viene richiamato ma non logga nulla in quanto fallisce il controllo sull’if..

    Grazie mille e ancora complimenti per il blog! :D

  8. 02 feb, 2012 Marco:

    dimenticato.. in OptionViewController il @syntetize del delegato l’ho messo :D

  9. 02 feb, 2012 Marco:

    Scusa lo spam.. ho notato che c’è un errore..
    ecco la correzione

    1
    2
    3
    4
    /** PrimaClasse.h **/
    #import "OptionViewController.h"

    @interface PrimaClasse: NSObject
  10. 02 feb, 2012 Giovambattista Fazioli:

    @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…

  11. 02 feb, 2012 Marco:

    Ti ringrazio moltissimo, mi hai illuminato :D
    ho risolto impostando

    1
    2
    3
    4
    5
    6
    7
    //OptionViewController.m
    - (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!

Lascia un commento

TAG XHTML PERMESSI: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> INSERIMENTO CODICE:
<pre></pre> // blocco generico
                   <code></code> // blocco generico
                   [cc_actionscript][/cc_actionscript] // Actionscript
                   [cc_actionscript3][/cc_actionscript3] // Actionscript 3
                   [cc_css][/cc_css] // CSS Style Sheet
                   [cc_html][/cc_html] // HTML
                   [cc_js][/cc_js] // Javascript
                   [cc_objc][/cc_objc] // Objective-C
                   [cc_php][/cc_objc] // PHP
                   [cc_sql][/cc_sql] // SQL