抑制警告“类别正在实施一种方法,该方法也将由其主要类实现”

时间:2012-02-24 01:14:13

标签: objective-c clang

我想知道如何压制警告:

  

类别正在实施一种方法,该方法也将由以下方式实施   它的主要班级。

我有一个特定的代码类别:

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

8 个答案:

答案 0 :(得分:341)

虽然所有人都说是正确的,但它实际上并没有回答你如何压制警告的问题。

如果由于某种原因必须使用此代码(在我的情况下,我在我的项目中使用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

答案 1 :(得分:63)

类别允许您向现有类添加新方法。如果要重新实现类中已存在的方法,通常需要创建子类而不是类。

Apple文档:Customizing existing classes

  

如果在一个声明的方法的名称   category类似于原始类中的方法,或者是in中的方法   同一类(甚至是超类)的另一个类别,   对于使用哪种方法实现,行为未定义   运行时。

在同一个类中具有完全相同签名的两个方法会导致不可预测的行为,因为每个调用者都无法指定他们想要的实现。

因此,您应该使用类别并为类提供新的和唯一的方法名称,或者如果要更改类中现有方法的行为,则应为子类。

答案 2 :(得分:20)

一个更好的选择(请参阅bneely的回答,为什么这个警告可以帮助您免受灾难)是使用方法调配。通过使用方法调配,您可以从类别中替换现有方法,而不必具有“获胜”的不确定性,同时保留调用旧方法的能力。秘诀是为覆盖提供不同的方法名称,然后使用运行时函数交换它们。

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

答案 3 :(得分:10)

在您的代码中尝试此操作:

+(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

答案 4 :(得分:5)

您可以使用方法调配来抑制此编译器警告。以下是当我们使用UITextBorderStyleNone的自定义背景时,我如何实现方法调整以在UITextField中绘制边距:

#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

答案 5 :(得分:2)

覆盖属性适用于类扩展(匿名类别),但不适用于常规类别。

根据Apple Docs使用类扩展(匿名类别),您可以创建公共类的专用接口,以便私有接口可以覆盖公开的属性。即你可以将属性从readonly更改为readwrite。

用例就是当您编写限制对公共属性的访问的库时,同一属性需要在库中进行完全读写访问。

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

搜索&#34; 使用类扩展程序隐藏隐私信息&#34;。

因此,此技术对类扩展有效,但对类别无效。

答案 6 :(得分:1)

类别是好事,但它们可能被滥用。 在编写类别时,您应该作为原则而不是重新实现现有方法。 这样做可能会导致奇怪的副作用,因为您现在正在重写另一个类所依赖的代码。你可以打破一个已知的类,并最终将调试器从内到外。 这只是糟糕的编程。

如果你需要它,你真的应该继承它。

然后提出了调侃,这对我来说是一个很大的NO-NO-NO。

在运行时将其打开是一个完整的NO-NO-NO。

你想要一个香蕉看起来像橙色,但只在运行时? 如果你想要橙色,那就写一个橙色。

不要让香蕉看起来像橙色。 更糟糕的是:不要把你的香蕉变成一个秘密特工,他会在世界各地悄悄地破坏香蕉以支持橘子。

糟糕!

答案 7 :(得分:1)

当我在类别中实现委托方法而不是主类时(即使没有主类实现),我遇到了这个问题。我的解决方案是将主类头文件移动到类别头文件 这很好用