En Objective-C, je peux ajouter des méthodes aux classes existantes avec une catégorie, par exemple.
@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end
Est-il également possible de faire cela avec des protocoles, c'est-à-dire s'il y avait un protocole NSString, quelque chose comme:
@interface <NSString> (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end
Je souhaite le faire, car j’ai plusieurs extensions de NSObject (la classe), en utilisant uniquement les méthodes NSObject publiques, et je veux que ces extensions fonctionnent également avec des objets implémentant le protocole.
Pour donner un autre exemple, si je veux écrire une méthode logDescription qui affiche la description d'un objet dans le journal:
- (void) logDescription {
NSLog(@"%@", [self description]);
}
Je peux bien sûr ajouter cette méthode à NSObject, mais il existe d'autres classes qui n'héritent pas de NSObject, où j'aimerais également avoir cette méthode, par exemple. NSProxy. Étant donné que la méthode utilise uniquement des membres publics du protocole, il serait préférable de l'ajouter au protocole.
Edit: Java 8 a maintenant cela avec "méthodes d'extension virtuelle" dans les interfaces: http://cr.openjdk.Java.net/~briangoetz/lambda/Defender%20Methods%20v4.pdf . C'est exactement ce que j'aimerais faire dans Objective-C. Je n'ai pas vu cette question gagner autant d'attention ...
Cordialement, Jochen
Réponse courte: non.
Réponse longue: comment cela fonctionnerait-il? Imaginez que vous pourriez ajouter des méthodes aux protocoles existants? Comment cela fonctionnerait-il? Imaginons que nous voulions ajouter une autre méthode à NSCoding, par exemple -(NSArray *) codingKeys;
. Cette méthode est une méthode obligatoire qui renvoie un tableau des clés utilisées pour coder l'objet.
Le problème est qu’il existe des classes existantes (comme, par exemple, NSString) qui implémentent déjà NSCoding, mais n’implémentent pas notre méthode codingKeys
. Que devrait-il arriver? Comment le framework pré-compilé saurait-il quoi faire lorsque ce message required / est envoyé à une classe qui ne l'implémente pas?
Vous pouvez dire "nous pouvons ajouter la définition de cette méthode via une catégorie" ou "nous pouvons dire que toutes les méthodes ajoutées via ces catégories de protocole sont explicitement facultatives". Oui, vous pouvez le faire et contourner théoriquement le problème que j'ai décrit ci-dessus. Mais si vous voulez le faire, vous pouvez tout aussi bien en faire une catégorie, puis vous assurer que la classe respondsToSelector:
avant d'appeler la méthode.
S'il est vrai que vous ne pouvez pas définir de catégories pour les protocoles (et que vous ne voudriez pas, car vous ne connaissez rien de l'objet existant), vous pouvez définir des catégories de telle sorte que le code ne s'applique qu'à un objet de le type donné qui a le protocole souhaité (un peu comme la spécialisation de modèle partielle de C++).
Ce type d’utilisation est principalement utilisé lorsque vous souhaitez définir une catégorie qui dépend d’une version personnalisée d’une classe. (Imaginez que j'ai des sous-classes UIViewController conformes au protocole Foo, ce qui signifie qu'elles ont la propriété foo, mon code de catégorie peut avoir besoin de la propriété foo, mais je ne peux pas l'appliquer au protocole Foo, et si je l'applique simplement Pour UIViewController, le code ne sera pas compilé par défaut et l'obliger à le compiler signifie que quelqu'un qui fait l'introspection, ou tout simplement bousiller, peut appeler votre code qui dépend du protocole. Une approche hybride pourrait fonctionner comme ceci:
@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
Avec l'approche hybride, vous pouvez écrire le code une seule fois (par classe supposée implémenter le protocole) et vous assurer qu'il n'affectera pas les instances de la classe qui ne sont pas conformes au protocole.
L'inconvénient est que la synthèse/définition des propriétés doit encore se produire dans les sous-classes individuelles.
Cela n'a pas vraiment de sens de le faire puisqu'un protocole ne peut pas réellement implémenter la méthode. Un protocole est une façon de déclarer que vous supportez certaines méthodes. L'ajout d'une méthode à cette liste en dehors du protocole signifie que toutes les classes "conformes" déclarent accidentellement la nouvelle méthode, même si elles ne l'implémentent pas. Si une classe implémentait le protocole NSObject mais ne descendait pas de NSObject et que vous ajoutiez ensuite une méthode au protocole, la conformité de la classe serait rompue.
Vous pouvez cependant créer un nouveau protocole qui inclut l'ancien avec une déclaration telle que @protocol SpecialObject <NSObject>
.
Adam Sharp a publié une solution qui a fonctionné pour moi.
Il comporte 3 étapes:
@optional
sur un protocole.Consultez le lien pour plus de détails.
si vous écrivez déjà une catégorie, pourquoi ne pas simplement ajouter la définition du protocole dans l'en-tête juste après la définition de la catégorie?
c'est à dire.
@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end
@protocol MyExtendedProtocolName <NSString>
//Method declarations go here
@end
de cette façon, toute classe qui importe l'en-tête de catégorie aura également la définition du protocole, et vous pourrez l'ajouter à votre classe.
@interface MyClass <OriginalProtocol,MyExtendedProtocolName>
de plus, soyez prudent lorsque vous sous-classez NSString, il s'agit d'un cluster et vous risquez de ne pas toujours avoir le comportement que vous attendez.
Je pense que vous pouvez mélanger les termes ici et là. Les extensions, catégories, protocoles, interfaces et classes sont tous différents dans Objective-C. Dans Le langage Objective-C 2.0 Apple décrit très bien les différences, notamment les avantages et les inconvénients liés à l’utilisation de catégories et d’extensions.
Si vous y réfléchissez, qu'est-ce qu'une "catégorie" ou une "extension" au sens conceptuel? C'est un moyen d'ajouter des fonctionnalités à une classe. En Objective-C, les protocoles sont conçus pour n'avoir aucune implémentation. Par conséquent, comment ajouteriez-vous ou étendriez-vous l'implémentation de quelque chose qui n'a pas d'implémentation pour commencer?