Suppress warning "Категория реализует метод, который также будет реализован его основным классом"

Мне было интересно, как подавить предупреждение:

Категория реализует метод, который также будет реализован его основной класс.

У меня есть это для определенной категории кода:

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

Ответ 1

Категория позволяет добавлять новые методы в существующий класс. Если вы хотите переопределить метод, который уже существует в классе, вы обычно создаете подкласс вместо категории.

Документация Apple: Настройка существующих классов

Если имя метода, объявленного в категория такая же, как метод в исходном классе, или метод в другая категория одного класса (или даже суперкласса), поведение undefined относительно того, какой метод реализации используется при во время выполнения.

Два метода с одной и той же сигнатурой в одном классе приведут к непредсказуемому поведению, поскольку каждый вызывающий не может указать, какую реализацию они хотят.

Итак, вы должны либо использовать категорию, либо предоставить имена методов, которые являются новыми и уникальными для класса или подкласса, если вы хотите изменить поведение существующего метода в классе.

Ответ 2

Хотя все, что сказано ниже, верное, на самом деле он не отвечает на ваш вопрос о том, как подавить предупреждение.

Если у вас есть этот код по какой-либо причине (в моем случае у меня есть HockeyKit в моем проекте, и они переопределяют метод в категории UIImage [edit: это уже не так]), и вам нужно получить свой проект для компиляции, вы можете использовать операторы #pragma для блокировки предупреждения так:

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

// do your override

#pragma clang diagnostic pop

Я нашел здесь информацию: http://www.cocoabuilder.com/archive/xcode/313767-disable-warning-for-override-in-category.html

Ответ 3

Лучшая альтернатива (см. ответ на вопрос, почему это предупреждение спасает вас от катастрофы) - это использовать метод swizzling. Используя метод swizzling, вы можете заменить существующий метод из категории без неопределенности того, кто "выигрывает", и сохраняя возможность вызова старого метода. Секрет заключается в том, чтобы дать переопределение имени другого метода, а затем поменять его на использование функций времени выполнения.

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

Затем определите свою пользовательскую реализацию:

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

Отмените реализацию по умолчанию с помощью:

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

Ответ 4

Попробуйте это в своем коде:

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

UPDATE2: добавьте этот макрос

#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

Ответ 5

Вы можете использовать метод swizzling для подавления этого предупреждения компилятора. Вот как я применил метод swizzling для рисования полей в UITextField, когда мы используем собственный фон с 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

Ответ 6

Категории - это хорошо, но их можно злоупотреблять. При написании категорий вы должны как принцип НЕ повторно внедрять выходящие методы. Это может вызвать странный побочный эффект, так как теперь вы переписываете код, который зависит от другого класса. вы можете сломать известный класс и в конечном итоге превратить ваш отладчик наизнанку. Это просто плохое программирование.

Если вам нужно сделать это, вы действительно должны подклассифицировать его.

Тогда предложение swizzling, то есть большое NO-NO-NO для меня.

Swinging во время выполнения - это полный NO-NO-NO.

Вы хотите, чтобы банан выглядел оранжевым, но только во время выполнения? Если вы хотите оранжевый, тогда напишите оранжевый цвет.

Не делай банановый взгляд и действуй как оранжевый. И что еще хуже: не превращайте свой банан в секретного агента, который спокойно саботирует бананы во всем мире в поддержку апельсинов.

Хлоп!

Ответ 7

У меня была эта проблема, когда я реализовал метод делегата в категории, а не основной класс (хотя основной реализации не было). Решение для меня состояло в том, чтобы перенести из основного заголовочного файла в заголовочный файл категории Это прекрасно работает

Ответ 8

Свойства верховой езды действительны для расширения класса (анонимная категория), но не для обычной категории.

В соответствии с Apple Docs с использованием расширения класса (анонимная категория) вы можете создать частный интерфейс для общедоступного класса, чтобы частный интерфейс мог переопределять общедоступные свойства. т.е. вы можете изменить свойство из readonly в readwrite.

Примером может служить случай, когда вы пишете библиотеки, ограничивающие доступ к общедоступным свойствам, в то время как одному и тому же свойству нужен полный доступ для чтения в библиотеке.

Ссылка на Документы Apple: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html

Найти " Использовать расширения классов для скрытия конфиденциальной информации".

Итак, этот метод действителен для расширения класса, но не для категории.