2009-06-22 8 views
10

Ho due classi che possono fungere da delegate di una terza classe, ed entrambe implementano un protocollo formale fatto interamente di metodi opzionali. Una delle classi implementa tutto mentre un'altra implementa solo un paio di metodi che mi interessano. Tuttavia, in fase di esecuzione quando ho la seconda classe come delegato alla terza classe e la terza classe finisce per chiamare uno dei metodi facoltativi non implementati su quel delegato, ottengo un errore di runtime che dice essenzialmente "Target non risponde a questo selettore di messaggi. " Pensavo che l'obiettivo-c gestisse correttamente questo caso e che non avrebbe fatto nulla se quel metodo non fosse effettivamente definito sulla classe. Potrebbe esserci qualcosa che mi manca?Perché i metodi di protocollo opzionali non implementati causano errori di runtime quando tale metodo viene chiamato in obj-c?

risposta

33

Quando si chiama un metodo facoltativo del delegato, è necessario assicurarsi che risponde al selettore prima di chiamare:

if ([delegate respondsToSelector:@selector(optionalMethod)]) 
    [delegate optionalMethod]; 
+2

Lo sospettavo, ma speravo non avrei bisogno di aggiungere quelli se-controlla tutto il codice. Grazie per il puntatore. – Kevlar

10

metodi Protocollo opzionale semplicemente significare l'oggetto che implementa il protocollo non deve implementare il metodo in questione - il callee quindi deve assolutamente verificare se l'oggetto implementa il metodo prima di chiamare (altrimenti si bloccherà, come notato). Queste categorie NSObject HOM possono essere utili:

@implementation NSObject (Extensions) 

- (id)performSelectorIfResponds:(SEL)aSelector 
{ 
    if ([self respondsToSelector:aSelector]) { 
     return [self performSelector:aSelector]; 
    } 
    return NULL; 
} 

- (id)performSelectorIfResponds:(SEL)aSelector withObject:(id)anObject 
{ 
    if ([self respondsToSelector:aSelector]) { 
     return [self performSelector:aSelector withObject:anObject]; 
    } 
    return NULL; 
} 

@end 

allora si può semplicemente fare:

[delegate performSelectorIfResponds:@selector(optionalMethod)]; 
+0

buoni suggerimenti :) – Kevlar

+0

........ Che cosa è HOM? – bandejapaisa

+0

HOM = Messaggistica ordine superiore. –

1

blocchi potrebbe fornire una soluzione migliore. Essi consentono di eseguire condizionale di qualsiasi codice basato sull'esistenza di un'implementazione di un dato metodo:

-(void) performBlock:(void (^)(void))block ifRespondsTo:(SEL) aSelector{ 
    if ([self respondsToSelector:aSelector]) { 
     block(); 
    } 
} 

Utilizzando questa aggiunta alla NSObject, è possibile condizionale eseguire qualsiasi metodo @optional, indipendentemente dal numero di parametri che potrebbe avere .

Vedi How to safely send @optional protocol messages that might not be implemented

4

Questa soluzione blocchi funziona bene, una volta che si ottiene la testa avvolta attorno a ciò che sta succedendo. Ho aggiunto un risultato BOOL perché volevo essere in grado di eseguire condizionatamente uno dei numerosi metodi facoltativi. Alcuni suggerimenti se si sta tentando di implementare questa soluzione:

Innanzitutto, se non si sono ancora incontrate le estensioni/categorie, è sufficiente aggiungerla all'inizio della classe, FUORI dalla definizione della classe esistente. Sarà un'estensione pubblica o privata basata su dove la metti.

@implementation NSObject (Extensions) 
// add block-based execution of optional protocol messages 
-(BOOL) performBlock:(void (^)(void))block ifRespondsTo:(SEL) aSelector 
{ 
    if ([self respondsToSelector:aSelector]) { 
     block(); 
     return YES; 
    } 
    return NO; 
} 
@end 

In secondo luogo, ecco come si chiama dal codice:

BOOL b = [(NSObject*)self.delegate performBlock:^{ 
    // code to run if the protocol method is implemented 
} 
ifRespondsTo:@selector(Param1:Param2:ParamN:)]; 

Sostituire Param1: Param2: paramN: con i nomi di ciascun parametro per il metodo di protocollo. Ognuno dovrebbe finire con due punti. Così, se il metodo di protocollo si presenta come:

-(void)dosomething:(id)blah withObj:(id)blah2 andWithObj(id)blah3;

l'ultima riga sarebbe simile a questa:

ifRespondsTo:@selector(dosomething:withObj:andWithObj:)];

Problemi correlati