So erweitern Sie Protokolle / Delegaten in Objective-C

73

Wenn ich eine Klasse wie AVAudioPlayer erweitern möchte, wie kann ich AVAudioPlayerDelegate am besten um eine weitere Methode erweitern? Mache ich eine Kategorie dafür, erweitere ich sie? Wenn ich es erweitere, muss ich dann auch sicherstellen, dass der eigentliche Delegate Getter / Setter überschrieben wird? Wie würde ich das Protokoll erweitern? Das Folgende gibt mir Fehler



@protocol AudioTrackDelegate : AVAudioPlayerDelegate {
    - (void)foo;
}
@end

@interface AudioTrack : AVAudioPlayer {
}
@end
schwindlig
quelle

Antworten:

131

Die Syntax zum Erstellen eines Protokolls, das ein anderes Protokoll implementiert, lautet wie folgt:

@protocol NewProtocol <OldProtocol>
- (void)foo;
@end

Wenn Sie eine Methode NewProtocolfür einen eingegebenen Zeiger aufrufen möchten, OldProtocolkönnen Sie entweder Folgendes aufrufen respondsToSelector:

if ([object respondsToSelector:@selector(foo)])
    [(id)object foo];

Oder definieren Sie Stub-Methoden als Kategorie für NSObject:

@interface NSObject (NewProtocol)
- (void)foo;
@end
@implementation NSObject (NewProtocol)
- (void)foo
{
}
@end
rpetrich
quelle
Wenn ich ein Protokoll erstelle, das ein anderes Protokoll implementiert ... Muss ich noch etwas tun, um sicherzustellen, dass die 'OldProtocol'-Methoden ausgelöst werden? weil für mich das neue Protokoll
ausgelöst wird
10
Ein Protokoll hat keine Methoden, es hat Methodendefinitionen; Das heißt, es beschreibt den Namen, die Argumente und die Rückgabetypen, die eine Klasse als "konform" mit diesem Protokoll betrachten muss. Durch Ihren Kommentar / Ihre Frage wette ich, dass Sie vergessen, [super setDelegate: value] in Ihrem -setDelegate aufzurufen:
rpetrich
Wenn Sie diesen Ansatz verwenden, werden die Methoden des neuen Protokolls nicht offengelegt. Schauen Sie sich meine Antwort für einen saubersten Weg an
Kappe
@ Simonthumper: Inwiefern ist es schrecklich?
rpetrich
Sie fügen einer Systemklasse, die überall verwendet wird, Methoden über eine Kategorie hinzu, die nichts mit NSObject zu tun haben. Es wird Ihren Code natürlich nicht beschädigen, aber es ist hackig, auch jedes einzelne Objekt, auf das Sie zugreifen, kann diese Methoden aufrufen, wodurch die Code-Vervollständigung Spaß macht!
Simonthumper
10

Denken Sie daran, dass ein Protokoll der kompilierten App keinen Code hinzufügt. Es erzwingt nur die Tatsache, dass Ihre Klasse die Methoden implementieren muss, um als "konform" mit dem Protokoll zu gelten. Eine gute Verwendung davon wäre, eine Gruppe von Klassen mit derselben Funktionsweise zu generieren: <printable>oder <serialized>usw. Sie könnten also ein <plays>Protokoll erstellen , zum Beispiel:

@protocol plays
    - (void) play;
    - (NSString *) type;
@end

Und dann muss eine Klasse, die konform ist, <plays>die Methoden playund implementieren type. Ist dies nicht der Fall, gibt der Compiler eine Warnung aus, kompiliert die Klasse jedoch trotzdem. In Ihrem Code prüfen Sie, ob ein Objekt einem Protokoll mit dem folgenden Code entspricht:

if ([obj conformsTo: @protocol(plays)]) {
    [obj play];
}

Eine Kategorie fügt Ihrer Klasse dynamisch neue Methoden hinzu. Diese Methoden sind für die Laufzeit als Selektoren global zugänglich und können wie in @selector(foo)und namentlich aufgerufen werden[object foo:bar];

Der Zweck einer Kategorie besteht darin, einer Klasse speziellen neuen Code hinzuzufügen, auch wenn Sie nicht über den Quellcode für diese Klasse verfügen. Möglicherweise treten Sicherheitsprobleme auf, und Sie können Speicherlecks in Klassen usw. verursachen.

In Ihrem Fall vielleicht in einer separaten Datei AVAudioPlayerDelegate_TrackOps.m

#import "AVAudioPlayerDelegate.h"
@implementation AVAudioPlayerDelegate (TrackOps)

- (NSObject *) foo {
    // do foo stuff;
    return bar;
}

@end

Wenn Sie es als Kategorie NSObjectfestlegen, reagieren alle Klassen darauf foo. Fookann auch eine eigenständige Methode sein Objc_perform_selector(@selector(foo)).

Fazit: Verwenden Sie eine Kategorie, um einer Klasse eine schnelle Methode hinzuzufügen, Protokolle zum Erzwingen von Methodenimplementierungen und Unterklassen, um vorhandene Klassen zu spezialisieren (z. B. Hinzufügen von Mitgliedsvariablen oder wichtige neue Funktionen). Kategorien können auch verwendet werden, um eine oder zwei Methoden zu überschreiben, wenn eine Unterklasse nicht benötigt und gewünscht wird. Wenn Sie jedoch einer Klasse Funktionen hinzufügen möchten, erstellen Sie normalerweise eine Unterklasse. Weitere Beispiele, Ideen und andere allgemeine Informationen zu diesem Thema finden Sie in der Apple-Einführung zu Objective-C

Matthew
quelle
Können Sie bitte erläutern, wie Sie einem Protokoll eine Kategorie hinzufügen können? Ich denke, etwas stimmt nicht mit der Syntax dessen, was Sie geschrieben haben. danke
schwindlig