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

Doz*_*Doz 97 objective-c clang

我想知道如何压制警告:

类别正在实施一种方法,该方法也将由其主要类实现.

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

+ (UIFont *)systemFontOfSize:(CGFloat)fontSize {
    return [self aCustomFontOfSize:fontSize];
}
Run Code Online (Sandbox Code Playgroud)

Ben*_*ron 341

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

如果由于某种原因你必须有这个代码(在我的情况下我在我的项目中有HockeyKit并且它们覆盖了UIImage类别中的方法[编辑:这不再是这种情况])并且你需要让你的项目编译,您可以使用#pragma语句来阻止警告,如下所示:

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

// do your override

#pragma clang diagnostic pop
Run Code Online (Sandbox Code Playgroud)

我在这里找到了这些信息:http://www.cocoabuilder.com/archive/xcode/313767-disable-warning-for-override-in-category.html

  • 这确实应该是公认的答案,因为它实际上回答了这个问题. (12认同)

bne*_*ely 63

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

Apple文档:自定义现有类

如果在类别中声明的方法的名称与原始类中的方法相同,或者在同一个类(或甚至是超类)上的另一个类别中的方法相同,则关于在哪个方法实现中使用哪个方法实现的行为是未定义的.运行.

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

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

  • 当单元测试一些包含单例的代码时,它会很有用.理想情况下,单例应作为协议注入代码,允许您切换实现.但是,如果已经在代码中嵌入了一个,则可以在单元测试中添加一个单例类别,并覆盖sharedInstance以及要控制的方法以将它们转换为虚拟对象. (4认同)

San*_*uja 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);
}
Run Code Online (Sandbox Code Playgroud)

然后定义您的自定义实现:

+ (UIFont *)mySystemFontOfSize:(CGFloat)fontSize {
...
}
Run Code Online (Sandbox Code Playgroud)

用你的覆盖默认实现:

MethodSwizzle([UIFont class], @selector(systemFontOfSize:), @selector(mySystemFontOfSize:));
Run Code Online (Sandbox Code Playgroud)


Vit*_*zuk 10

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

+(void)load{
    EXCHANGE_METHOD(Method1, Method1Impl);
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)


Say*_*nuj 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
Run Code Online (Sandbox Code Playgroud)