it-swarm.com.de

Definieren von Kategorien für Protokolle in Objective-C?

In Objective-C kann ich vorhandenen Klassen Methoden mit einer Kategorie hinzufügen, z.

@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end

Ist es auch möglich, dies mit Protokollen zu tun, d. H. Wenn es ein NSString-Protokoll gab, so etwas wie:

@interface <NSString> (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end

Ich möchte dies tun, da ich mehrere Erweiterungen für NSObject (die Klasse) habe, die nur öffentliche NSObject-Methoden verwenden, und ich möchte, dass diese Erweiterungen auch mit Objekten arbeiten, die das Protokoll implementieren.

Um ein weiteres Beispiel zu geben, was ist, wenn ich eine Methode logDescription schreiben möchte, die die Beschreibung eines Objekts in das Protokoll druckt:

- (void) logDescription {
    NSLog(@"%@", [self description]);
}

Ich kann diese Methode natürlich zu NSObject hinzufügen, aber es gibt andere Klassen, die nicht von NSObject erben, wo ich diese Methode auch haben möchte, z. NSProxy. Da die Methode nur öffentliche Mitglieder des Protokolls verwendet, ist es am besten, sie dem Protokoll hinzuzufügen.

Bearbeiten: Java 8 hat jetzt diese mit "virtuellen Erweiterungsmethoden" in Schnittstellen: http://cr.openjdk.Java.net/~briangoetz/lambda/Defender%20Methods%20v4.pdf . Genau das möchte ich in Objective-C tun. Ich habe nicht gesehen, dass diese Frage so viel Aufmerksamkeit erregt hat ...

Grüße, Jochen

37
Jochen

в extObjC есть элемент sauberste вы можете делать с протоколами/категориями ... в первую очередь это @concreteprotocol...

  • Определяет "конкретный протокол", который может предоставлять реализации методов по умолчанию по.
  • Блок @protocol должен существовать в заголовочном файле, а соответствующий блок @concreteprotocol - в файле реализации.
  • Любой объект, который объявляет себя соответствующим этому протоколу, получит свои реализации метода, но только если ни один метод с таким именем уже не существует.

MyProtocol.h

@protocol MyProtocol 
@required - (void)someRequiredMethod;
@optional - (void)someOptionalMethod;
@concrete - (BOOL)isConcrete;   

MyProtocol.m

 @concreteprotocol(MyProtocol) - (BOOL)isConcrete { return YES; } ...

поэтому объявление объекта MyDumbObject : NSObject <MyProtocol> автоматически вернет YES в isConcrete.

Кроме того, они имеют pcategoryinterface(PROTOCOL,CATEGORY), которая "определяет интерфейс для категории с именем CATEGORY в протонем". Категории протокола содержат методы, которые автоматически применяются к любому классу, которы. "Существует сопутствующий макрос, который вы также должны использовать в своем файле реализаци.

И наконец, но НЕ в последнюю очередь/не , непосредственно связанный с @protocols, это synthesizeAssociation(CLASS, PROPERTY), который „синтезирует свойство для класса с использованием связанных объектов. Это в первую очередь полезно для добавления свойств в класс внутри Категория. ИМУЩЕСТВО должно быть объявлено с @property в интерфейсе указанного класса (или категории на нем) и должно иметь тип объекта. "

Так много инструментов в этой библиотеке открывают (повышают) возможности, которые вы можете делать с ObjC ... от множественного наследования ... до нуля Ваше воображение - это предел.

18
Alex Gray

Kurze Antwort: Nein.

Lange Antwort: Wie würde das funktionieren? Stellen Sie sich vor, Sie könnten Methoden zu vorhandenen Protokollen hinzufügen? Wie würde das funktionieren? Stellen Sie sich vor, wir wollten eine andere Methode zu NSCoding hinzufügen, etwa -(NSArray *) codingKeys;. Diese Methode ist eine erforderliche Methode, die ein Array der Schlüssel zurückgibt, die zum Codieren des Objekts verwendet werden.

Das Problem ist, dass es Klassen gibt (wie zum Beispiel NSString), die bereits NSCoding implementieren, aber unsere codingKeys-Methode nicht implementieren. Was soll passieren? Wie würde das vorkompilierte Framework wissen, was zu tun ist, wenn diese required - Nachricht an eine Klasse gesendet wird, die sie nicht implementiert?

Man könnte sagen "wir können die Definition dieser Methode über eine Kategorie hinzufügen" oder "wir könnten sagen, dass alle über diese Protokollkategorien hinzugefügten Methoden explizit optional sind". Ja, Sie könnten dies tun und theoretisch das oben beschriebene Problem umgehen. Aber wenn Sie das tun, können Sie es gleich zu einer Kategorie machen und dann prüfen, ob die Klasse respondsToSelector: vor dem Aufrufen der Methode sicher ist.

25
Dave DeLong

Es ist zwar wahr, dass Sie keine Kategorien für Protokolle definieren können (und dies nicht möchten, da Sie nichts über das vorhandene Objekt wissen), Sie können Kategorien jedoch so definieren, dass der Code nur für ein Objekt von gilt der angegebene Typ, der das gewünschte Protokoll hat (eine Art wie die teilweise Spezialisierung von C++).

Die Hauptanwendung für so etwas ist, wenn Sie eine Kategorie definieren möchten, die von einer benutzerdefinierten Version einer Klasse abhängt. (Stellen Sie sich vor, ich habe UIViewController-Unterklassen, die dem Foo-Protokoll entsprechen. Dies bedeutet, dass sie die Eigenschaft foo haben. Mein Kategoriecode muss möglicherweise die Eigenschaft foo haben. Ich kann sie jedoch nicht auf das Protokoll von Foo anwenden Für UIViewController wird der Code nicht standardmäßig kompiliert, und wenn er zum Kompilieren gezwungen wird, bedeutet dies, dass jemand, der Introspektion durchführt oder einfach nur vermasselt, Ihren Code je nach Protokoll aufruft. Ein hybrider Ansatz könnte wie folgt funktionieren:

@protocol Foo
- (void)fooMethod

@property (retain) NSString *foo;
@end

@implementation UIViewController (FooCategory)

- (void)fooMethod {
    if (![self conformsToProtocol:@protocol(Foo)]) {
        return;
    }

    UIViewController<Foo> *me = (UIViewController<Foo>*) self;
    // For the rest of the method, use "me" instead of "self"
    NSLog(@"My foo property is \"%@\"", me.foo);
}
@end

Mit dem hybriden Ansatz können Sie den Code nur einmal schreiben (pro Klasse, die das Protokoll implementieren soll) und sicherstellen, dass er keine Instanzen der Klasse beeinflusst, die nicht dem Protokoll entsprechen.

Der Nachteil ist, dass die Eigenschaftssynthese/Definition noch in den einzelnen Unterklassen erfolgen muss.

22
Douglas Mayle

Dies ist nicht wirklich sinnvoll, da ein Protokoll die Methode nicht wirklich implementieren kann. Ein Protokoll ist eine Möglichkeit, zu erklären, dass Sie einige Methoden unterstützen. Das Hinzufügen einer Methode zu dieser Liste außerhalb des Protokolls bedeutet, dass alle "konformen" Klassen versehentlich die neue Methode deklarieren, auch wenn sie sie nicht implementieren. Wenn eine Klasse das NSObject-Protokoll implementiert, aber nicht von NSObject abstammt und Sie dem Protokoll eine Methode hinzugefügt haben, würde dies die Konformität der Klasse beeinträchtigen.

Sie können jedoch ein neues Protokoll erstellen, das das alte Protokoll mit einer Deklaration wie @protocol SpecialObject <NSObject> enthält.

7
Chuck

Adam Sharp hat eine Lösung gepostet die für mich funktioniert hat.

Es umfasst 3 Schritte:

  1. Definieren der Methoden, die Sie in einem Protokoll als @optional hinzufügen möchten.
  2. Die Objekte, die Sie erweitern möchten, müssen mit diesem Protokoll übereinstimmen.
  3. Kopieren dieser Methoden zur Laufzeit in diese Objekte.

Überprüfen Sie den Link für die vollständigen Details.

0
Senseful

wenn Sie bereits eine Kategorie schreiben, können Sie die Protokolldefinition direkt nach der Kategoriedefinition in die Kopfzeile einfügen.

d.h.

@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end

@protocol MyExtendedProtocolName <NSString>
//Method declarations go here
@end

auf diese Weise erhält jede Klasse, die den Kategorieheader importiert, auch die Protokolldefinition, und Sie können sie in Ihre Klasse aufnehmen.

@interface MyClass <OriginalProtocol,MyExtendedProtocolName>

seien Sie vorsichtig, wenn Sie NSString subclassing. Es handelt sich um einen Cluster, und Sie erhalten möglicherweise nicht immer das Verhalten, das Sie erwarten.

0
pxl

Ich denke, Sie vermischen hier und da die Begriffe. Erweiterungen, Kategorien, Protokolle, Schnittstellen und Klassen sind alles andere in Objective-C. In The Objective-C 2.0 Language beschreibt Apple die Unterschiede sehr gut, einschließlich der Vor- und Nachteile der Verwendung von Kategorien und Erweiterungen.

Wenn Sie darüber nachdenken, was ist eine "Kategorie" oder "Erweiterung" im konzeptionellen Sinne? Es ist eine Möglichkeit, einer Klasse Funktionalität hinzuzufügen. In Objective-C haben Protokolle keine Implementierung. Wie würden Sie also die Implementierung von etwas hinzufügen oder erweitern, für das noch keine Implementierung erforderlich ist?

0
slf