我想知道如何抑制警告:
Category正在实现一种方法,该方法也将由其主类实现。
我有一个特定的代码类别:
+ (UIFont *)systemFontOfSize:(CGFloat)fontSize {
return [self aCustomFontOfSize:fontSize];
}
我想知道如何抑制警告:
Category正在实现一种方法,该方法也将由其主类实现。
我有一个特定的代码类别:
+ (UIFont *)systemFontOfSize:(CGFloat)fontSize {
return [self aCustomFontOfSize:fontSize];
}
Answers:
类别使您可以向现有类添加新方法。如果要重新实现该类中已经存在的方法,则通常创建一个子类而不是类别。
Apple文档:自定义现有类
如果在一个类别中声明的方法的名称与原始类中的方法的名称相同,或者在同一类(甚至是超类)的另一个类别中的方法的名称相同,则该行为在使用哪种方法实现时未定义运行。
在同一个类中具有完全相同的签名的两个方法将导致无法预测的行为,因为每个调用者都无法指定他们想要的实现。
因此,如果要更改类中现有方法的行为,则应该使用类别并提供该类的新名称和唯一方法名称,或者使用子类。
尽管所有的转瞬即逝都是正确的,但实际上并没有回答您如何抑制警告的问题。
如果您由于某种原因必须包含此代码(就我而言,我的项目中有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
更好的替代方法(请参阅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:));
在您的代码中尝试以下操作:
+(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
您可以使用swizzling方法抑制此编译器警告。这是当我们将自定义背景与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
重写属性对类扩展(匿名类别)有效,但对常规类别无效。
根据使用类扩展名(匿名类别)的Apple Docs,您可以为公共类创建私有接口,这样私有接口可以覆盖公开显示的属性。即,您可以将属性从只读更改为读写。
一个用例是编写限制公共属性访问的库,而同一属性需要在库内具有完全读写访问权限。
Apple Docs链接:https : //developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html
搜索“ 使用类扩展来隐藏私人信息 ”。
因此,此技术对类扩展有效,但对类别无效。
类别是一件好事,但它们可能会被滥用。编写类别时,原则上不应重新实现现有方法。这样做可能会引起奇怪的副作用,因为您现在正在重新编写另一个类所依赖的代码。您可能会破坏一个已知的类,并最终使调试器由内而外。这简直是糟糕的编程。
如果需要执行此操作,则确实应该将其子类化。
然后,有个令人毛骨悚然的建议,这对我来说是一个很大的禁忌。
在运行时模糊它是一个完全的NO-NO-NO。
您想让香蕉看起来像橙色,但仅在运行时?如果要橙色,请写一个橙色。
不要让香蕉看起来像橘子。更糟糕的是:不要将您的香蕉变成秘密特工,他会在全世界范围内悄悄破坏香蕉以支持桔子。
kes!
super
否则调用它。