Подавить предупреждение "Категория реализует метод, который также будет реализован его основным классом" - PullRequest
97 голосов
/ 24 февраля 2012

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

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

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

Ответы [ 8 ]

341 голосов
/ 08 марта 2012

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

Если по какой-то причине вам нужен этот код (в моем случае у меня есть HockeyKit вмой проект, и они переопределяют метод в категории UIImage [править: это уже не так]), и вам нужно получить проект для компиляции, вы можете использовать операторы #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

63 голосов
/ 24 февраля 2012

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

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

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

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

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

20 голосов
/ 25 сентября 2012

Лучшей альтернативой (см. Ответ Бнили, почему это предупреждение спасает вас от катастрофы) является использование метода 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:));
10 голосов
/ 13 августа 2012

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

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

ОБНОВЛЕНИЕ2: Добавить этот макрос

#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 голосов
/ 22 февраля 2013

Вы можете использовать метод 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
2 голосов
/ 23 декабря 2014

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

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

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

Ссылка на Apple Docs: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html

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

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

1 голос
/ 28 ноября 2012

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

1 голос
/ 06 ноября 2012

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

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

Тогда предложение мошенничества - это большое НЕТ-НЕТ-НЕТ для меня.

Смахивание во время выполнения - это полная НЕТ-НЕТ-НЕТ.

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

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

Yikes!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...