Suppress warning "Category is implementing a method which will also be implemented by its primary class"

Objective CClang

Objective C Problem Overview


I was wondering how to suppress the warning:

> Category is implementing a method which will also be implemented by > its primary class.

I have this for a specific code category:

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

Objective C Solutions


Solution 1 - Objective C

Although everything bneely said is correct, it doesn't actually answer your question of how to suppress the warning.

If you have to have this code in for some reason (in my case I have HockeyKit in my project and they override a method in a UIImage category [edit: this is no longer the case]) and you need to get your project to compile, you can use #pragma statements to block the warning like so:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"

// do your override

#pragma clang diagnostic pop

I found the information here: http://www.cocoabuilder.com/archive/xcode/313767-disable-warning-for-override-in-category.html

Solution 2 - Objective C

A category allows you to add new methods to an existing class. If you want to reimplement a method that already exists in the class, you typically create a subclass instead of a category.

Apple documentation: Customizing existing classes

> If the name of a method declared in a > category is the same as a method in the original class, or a method in > another category on the same class (or even a superclass), the > behavior is undefined as to which method implementation is used at > runtime.

Two methods with the exact same signature in the same class would lead to unpredictable behavior, because each caller cannot specify which implementation they want.

So, you should either use a category and provide method names that are new and unique for the class, or subclass if you want to change the behavior of an existing method in a class.

Solution 3 - Objective C

A better alternative (see bneely's answer to why this warning is saving you from disaster) is to use method swizzling. By using method swizzling, you can replace an existing method from a category without the uncertainty of who "wins", and while preserving the ability to call through to the old method. The secret is to give the override a different method name, then swap them using runtime functions.

#import <objc/runtime.h> 
#import <objc/message.h>

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);
}

Then define your custom implementation:

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

Override the default implementation with yours:

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

Solution 4 - Objective C

Try this in your code:

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

UPDATE2: Add this macro

#import <Foundation/Foundation.h>
#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 <objc/runtime.h>

@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

Solution 5 - Objective C

You can use method swizzling to suppress this compiler warning. Here is how I implemented method swizzling for drawing margins in a UITextField when we use a custom background with UITextBorderStyleNone:

#import <UIKit/UIKit.h>

@interface UITextField (UITextFieldCatagory)

+(void)load;
- (CGRect)textRectForBoundsCustom:(CGRect)bounds;
- (CGRect)editingRectForBoundsCustom:(CGRect)bounds;
@end

#import "UITextField+UITextFieldCatagory.h"
#import <objc/objc-runtime.h>

@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
 

Solution 6 - Objective C

Over-riding properties is valid for a Class Extension (Anonymous Category), but not for a regular Category.

According to Apple Docs using a Class Extension (Anonymous Category) you can create a Private interface to a public class, such that the private interface can override the publicly exposed properties. i.e. you can change a property from readonly to readwrite.

A use case for this is when you write libraries that restrict access to public properties, while the same property needs full read write access within the library.

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

Search for "Use Class Extensions to Hide Private Information".

So this technique is valid for a Class Extension, but not for a Category.

Solution 7 - Objective C

I had this problem when I implemented a delegate method in a category rather that the main class ( even though there was no main class implementation). The solution for me was to move the from the main class header file to the category header file This works fine

Solution 8 - Objective C

Categories are a good thing, but they can be abused. When writing categories your should as a principle NOT re-implement exiting methods. Doing so may cause strange side effect as you are now re-writing code that another class depends one. you could break a known class, and end up turning your debugger inside-out. It is simply bad programming.

If you need todo it, you really should subclass it.

Then the suggestion of swizzling, that is a big NO-NO-NO to me.

Swizzing it at runtime is a complete NO-NO-NO.

You want a banana to look like an orange, but only at runtime? If you want an orange, then write an orange.

Don not make a banana look and act like an orange. And worse: dont turn your banana into a secret agent who will quietly sabotaging bananas worldwide in support of oranges.

Yikes!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDozView Question on Stackoverflow
Solution 1 - Objective CBen BaronView Answer on Stackoverflow
Solution 2 - Objective CbneelyView Answer on Stackoverflow
Solution 3 - Objective CSonny SalujaView Answer on Stackoverflow
Solution 4 - Objective CVitaliy GervazukView Answer on Stackoverflow
Solution 5 - Objective CSay2ManujView Answer on Stackoverflow
Solution 6 - Objective CKris SubramanianView Answer on Stackoverflow
Solution 7 - Objective CgheeseView Answer on Stackoverflow
Solution 8 - Objective CLeanderView Answer on Stackoverflow