Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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];
}
like image 688
Doz Avatar asked Feb 24 '12 01:02

Doz


4 Answers

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

like image 54
Ben Baron Avatar answered Oct 17 '22 05:10

Ben Baron


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.

like image 31
bneely Avatar answered Oct 17 '22 04:10

bneely


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:));
like image 20
Sonny Saluja Avatar answered Oct 17 '22 05:10

Sonny Saluja


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
like image 10
Vitaliy Gervazuk Avatar answered Oct 17 '22 05:10

Vitaliy Gervazuk