2009-06-22 5 views
10

Ich habe zwei Klassen, die als Delegat einer dritten Klasse fungieren können, und beide implementieren ein formales Protokoll, das ausschließlich aus optionalen Methoden besteht. Eine der Klassen implementiert alles, während eine andere nur einige Methoden implementiert, die mir wichtig sind. Zur Laufzeit, wenn ich die zweite Klasse als Delegat für die dritte Klasse habe und die dritte Klasse schließlich eine der nicht implementierten optionalen Methoden für diesen Delegaten aufruft, erhalte ich einen Laufzeitfehler, der im Wesentlichen lautet: "Ziel reagiert nicht darauf Nachrichtenauswahl. " Ich dachte, dass objective-c diesen Fall richtig behandelt und dass es nichts tun würde, wenn diese Methode nicht wirklich in der Klasse definiert wäre. Könnte es etwas geben, das ich vermisse?Warum verursachen nicht implementierte optionale Protokollmethoden Laufzeitfehler, wenn diese Methode in obj-c aufgerufen wird?

Antwort

33

Wenn Sie eine optionale Methode Ihrer Delegierten aufrufen, müssen Sie sicherstellen, dass es an den Wähler reagiert es vor dem Aufruf:

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

ich so viel vermutet wird, aber ich hatte gehofft, dass ich nicht hinzufügen müssten Diese If-Checks über den gesamten Code. Danke für den Zeiger. – Kevlar

10

Optional Protokoll Methoden einfach bedeuten, das Objekt das Protokoll implementiert haben nicht zu implementieren die betreffende Methode - der Angerufene muss dann unbedingt prüfen, ob das Objekt die Methode vor dem Aufruf implementiert (sonst stürzt man ab, wie Sie bemerkt haben). Diese NSObject HOM Kategorien können hilfreich sein:

@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 

Dann können Sie einfach tun:

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

nette Vorschläge :) – Kevlar

+0

........ Was ist HOM? – bandejapaisa

+0

HOM = Nachricht höherer Ordnung. –

1

Blocks könnte eine bessere Lösung. Sie ermöglichen bedingt beliebigen Code auszuführen, basierend auf der Existenz einer Implementierung eines gegebenen Methode:

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

Durch die Nutzung dieser zusätzlich zu NSObject, können Sie bedingt jede @optional Methode ausführen, unabhängig davon, wie viele Parameter könnte es haben .

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

4

Diese Blöcke Lösung funktioniert gut, wenn Sie Ihren Kopf um eingewickelt bekommen, was los ist. Ich habe ein BOOL-Ergebnis hinzugefügt, weil ich in der Lage sein wollte, eine von mehreren optionalen Methoden bedingt auszuführen. Einige Tipps, wenn Sie versuchen, diese Lösung zu implementieren:

Erstens, wenn Sie Erweiterung/Kategorien noch nicht gefunden haben, fügen Sie einfach diese an den Anfang Ihrer Klasse, außerhalb der vorhandenen Klassendefinition. Es wird eine öffentliche oder private Erweiterung sein, je nachdem, wo Sie es hinstellen.

@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 

Zweitens ist hier, wie Sie es von Ihrem Code aufrufen:

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

ersetzen Param1: Param2: ParamN: mit den Namen der einzelnen Parameter für die Protokollverfahren. Jeder sollte mit einem Doppelpunkt enden. Also, wenn Ihr Protokoll Methode wie folgt aussieht:

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

die letzte Zeile würde wie folgt aussehen:

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

Verwandte Themen