Supprimer l’avertissement «Catégorie met en œuvre une méthode qui sera également implémentée par sa classe primaire»

Je me demandais comment supprimer l’avertissement:

La catégorie met en œuvre une méthode qui sera également implémentée par sa classe primaire.

J’ai ceci pour une catégorie de code spécifique:

+ (UIFont *)systemFontOfSize:(CGFloat)fontSize { return [self aCustomFontOfSize:fontSize]; } 

Une catégorie vous permet d’append de nouvelles méthodes à une classe existante. Si vous souhaitez réimplémenter une méthode qui existe déjà dans la classe, vous créez généralement une sous-classe au lieu d’une catégorie.

Documentation Apple: Personnalisation des classes existantes

Si le nom d’une méthode déclarée dans une catégorie est identique à une méthode de la classe d’origine ou d’une méthode d’une autre catégorie de la même classe (ou même d’une super-classe), le comportement n’est pas défini. runtime

Deux méthodes ayant exactement la même signature dans la même classe entraîneraient un comportement imprévisible, car chaque appelant ne peut pas spécifier quelle implémentation il souhaite.

Vous devez donc utiliser une catégorie et fournir des noms de méthodes nouveaux et uniques pour la classe ou la sous-classe si vous souhaitez modifier le comportement d’une méthode existante dans une classe.

Bien que tout ce qui est dit soit correct, cela ne répond pas à votre question sur la manière de supprimer l’avertissement.

Si vous devez avoir ce code pour une raison quelconque (dans mon cas, j’ai HockeyKit dans mon projet et ils remplacent une méthode dans une catégorie UIImage [edit: ce n’est plus le cas]) et vous devez comstackr votre projet , vous pouvez utiliser des instructions #pragma pour bloquer l’avertissement comme suit:

 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation" // do your override #pragma clang diagnostic pop 

J’ai trouvé l’information ici: http://www.cocoabuilder.com/archive/xcode/313767-disable-warning-for-override-in-category.html

Une meilleure alternative (voir la réponse de Bneely à pourquoi cet avertissement vous sauve de la catastrophe) est d’utiliser la méthode swizzling. En utilisant la méthode swizzling, vous pouvez remplacer une méthode existante par une catégorie sans l’incertitude de savoir qui “gagne”, tout en conservant la possibilité d’appeler l’ancienne méthode. Le secret consiste à donner à la substitution un nom de méthode différent, puis à les échanger à l’aide de fonctions d’exécution.

 #import  #import  void MethodSwizzle(Class c, SEL orig, SEL new) { Method origMethod = class_getInstanceMethod(c, orig); Method newMethod = class_getInstanceMethod(c, new); if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); else method_exchangeImplementations(origMethod, newMethod); } 

Définissez ensuite votre implémentation personnalisée:

 + (UIFont *)mySystemFontOfSize:(CGFloat)fontSize { ... } 

Remplacez l’implémentation par défaut par la vôtre:

 MethodSwizzle([UIFont class], @selector(systemFontOfSize:), @selector(mySystemFontOfSize:)); 

Essayez ceci dans votre code:

 +(void)load{ EXCHANGE_METHOD(Method1, Method1Impl); } 

UPDATE2: Ajouter cette macro

 #import  #define EXCHANGE_METHOD(a,b) [[self class]exchangeMethod:@selector(a) withNewMethod:@selector(b)] @interface NSObject (MethodExchange) +(void)exchangeMethod:(SEL)origSel withNewMethod:(SEL)newSel; @end #import  @implementation NSObject (MethodExchange) +(void)exchangeMethod:(SEL)origSel withNewMethod:(SEL)newSel{ Class class = [self class]; Method origMethod = class_getInstanceMethod(class, origSel); if (!origMethod){ origMethod = class_getClassMethod(class, origSel); } if (!origMethod) @throw [NSException exceptionWithName:@"Original method not found" reason:nil userInfo:nil]; Method newMethod = class_getInstanceMethod(class, newSel); if (!newMethod){ newMethod = class_getClassMethod(class, newSel); } if (!newMethod) @throw [NSException exceptionWithName:@"New method not found" reason:nil userInfo:nil]; if (origMethod==newMethod) @throw [NSException exceptionWithName:@"Methods are the same" reason:nil userInfo:nil]; method_exchangeImplementations(origMethod, newMethod); } @end 

Vous pouvez utiliser la méthode swizzling pour supprimer cet avertissement du compilateur. Voici comment j’ai implémenté la méthode swizzling pour dessiner des marges dans un UITextField lorsque nous utilisons un arrière-plan personnalisé avec UITextBorderStyleNone:

 #import  @interface UITextField (UITextFieldCatagory) +(void)load; - (CGRect)textRectForBoundsCustom:(CGRect)bounds; - (CGRect)editingRectForBoundsCustom:(CGRect)bounds; @end #import "UITextField+UITextFieldCatagory.h" #import  @implementation UITextField (UITextFieldCatagory) +(void)load { Method textRectForBounds = class_getInstanceMethod(self, @selector(textRectForBounds:)); Method textRectForBoundsCustom = class_getInstanceMethod(self, @selector(textRectForBoundsCustom:)); Method editingRectForBounds = class_getInstanceMethod(self, @selector(editingRectForBounds:)); Method editingRectForBoundsCustom = class_getInstanceMethod(self, @selector(editingRectForBoundsCustom:)); method_exchangeImplementations(textRectForBounds, textRectForBoundsCustom); method_exchangeImplementations(editingRectForBounds, editingRectForBoundsCustom); } - (CGRect)textRectForBoundsCustom:(CGRect)bounds { CGRect inset = CGRectMake(bounds.origin.x + 10, bounds.origin.y, bounds.size.width - 10, bounds.size.height); return inset; } - (CGRect)editingRectForBoundsCustom:(CGRect)bounds { CGRect inset = CGRectMake(bounds.origin.x + 10, bounds.origin.y, bounds.size.width - 10, bounds.size.height); return inset; } @end 

Les propriétés de dépassement sont valables pour une extension de classe (catégorie anonyme), mais pas pour une catégorie régulière.

Selon Apple Docs utilisant une extension de classe (catégorie anonyme), vous pouvez créer une interface privée pour une classe publique, de sorte que l’interface privée puisse remplacer les propriétés exposées publiquement. c’est-à-dire que vous pouvez changer une propriété de readonly en readwrite.

Un cas d’utilisation est lorsque vous écrivez des bibliothèques qui restreignent l’access aux propriétés publiques, tandis que la même propriété nécessite un access en lecture-écriture complet dans la bibliothèque.

Lien Apple Docs: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html

Recherchez ” Utiliser des extensions de classe pour masquer des informations privées “.

Cette technique est donc valable pour une extension de classe, mais pas pour une catégorie.

Les catégories sont une bonne chose, mais elles peuvent être abusées. Lorsque vous écrivez des catégories, vous ne devez en principe pas réimplémenter les méthodes existantes. Cela peut provoquer des effets secondaires étranges, car vous réécrivez maintenant le code dont dépend une autre classe. vous pourriez rompre une classe connue et finir par transformer votre débogueur à l’envers. C’est simplement une mauvaise programmation.

Si vous avez besoin de le faire, vous devriez vraiment le sous-classer.

Ensuite, la suggestion de swizzling, c’est un grand non à moi non.

Swizzing à l’exécution est un NO-NO-NO complet.

Vous voulez qu’une banane ressemble à une orange, mais seulement à l’exécution? Si vous voulez une orange, écrivez une orange.

Ne pas faire une banane et agir comme une orange. Et pire encore: ne transformez pas votre banane en un agent secret qui sabotera discrètement les bananes dans le monde entier pour soutenir les oranges.

Yikes!

J’ai eu ce problème quand j’ai implémenté une méthode de délégué dans une catégorie plutôt que la classe principale (même s’il n’y avait pas d’implémentation de classe principale). La solution pour moi était de déplacer le fichier d’en-tête de la classe principale vers le fichier d’en-tête de la catégorie. Cela fonctionne bien