我想知道如何压制警告:
类别正在实施一种方法,该方法也将由以下方式实施 它的主要班级。
我有一个特定的代码类别:
+ (UIFont *)systemFontOfSize:(CGFloat)fontSize {
return [self aCustomFontOfSize:fontSize];
}
答案 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。
用例就是当您编写限制对公共属性的访问的库时,同一属性需要在库中进行完全读写访问。
搜索&#34; 使用类扩展程序隐藏隐私信息&#34;。
因此,此技术对类扩展有效,但对类别无效。
答案 6 :(得分:1)
类别是好事,但它们可能被滥用。 在编写类别时,您应该作为原则而不是重新实现现有方法。 这样做可能会导致奇怪的副作用,因为您现在正在重写另一个类所依赖的代码。你可以打破一个已知的类,并最终将调试器从内到外。 这只是糟糕的编程。
如果你需要它,你真的应该继承它。
然后提出了调侃,这对我来说是一个很大的NO-NO-NO。
在运行时将其打开是一个完整的NO-NO-NO。
你想要一个香蕉看起来像橙色,但只在运行时? 如果你想要橙色,那就写一个橙色。
不要让香蕉看起来像橙色。 更糟糕的是:不要把你的香蕉变成一个秘密特工,他会在世界各地悄悄地破坏香蕉以支持橘子。
糟糕!
答案 7 :(得分:1)
当我在类别中实现委托方法而不是主类时(即使没有主类实现),我遇到了这个问题。我的解决方案是将主类头文件移动到类别头文件 这很好用