警告を抑制する方法を考えていました:
カテゴリは、そのプライマリクラスによって実装されるメソッドを実装しています。
私は特定のコードカテゴリに対してこれを持っています:
+ (UIFont *)systemFontOfSize:(CGFloat)fontSize {
return [self aCustomFontOfSize:fontSize];
}
警告を抑制する方法を考えていました:
カテゴリは、そのプライマリクラスによって実装されるメソッドを実装しています。
私は特定のコードカテゴリに対してこれを持っています:
+ (UIFont *)systemFontOfSize:(CGFloat)fontSize {
return [self aCustomFontOfSize:fontSize];
}
回答:
カテゴリを使用すると、新しいメソッドを既存のクラスに追加できます。クラスにすでに存在するメソッドを再実装する場合は、通常、カテゴリではなくサブクラスを作成します。
Appleドキュメント:既存のクラスのカスタマイズ
カテゴリで宣言されたメソッドの名前が元のクラスのメソッド、または同じクラス(またはスーパークラス)の別のカテゴリのメソッドと同じである場合、どのメソッドの実装が使用されるかについての動作は定義されていません。ランタイム。
同じクラスでまったく同じシグネチャを持つ2つのメソッドは、各呼び出し元が必要な実装を指定できないため、予期しない動作を引き起こします。
したがって、カテゴリを使用して、クラスに対して新しい一意のメソッド名を指定するか、クラスの既存のメソッドの動作を変更する場合はサブクラスを指定する必要があります。
ひどいことはすべて正しいですが、実際には警告を抑制する方法についてのあなたの質問に答えるものではありません。
何らかの理由でこのコードを含める必要がある場合(私の場合、プロジェクトに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
より良い代替案(この警告が災害からあなたを救う理由についての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
メソッドのスウィズリングを使用して、このコンパイラの警告を抑制できます。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によると、パブリックインターフェイスへのプライベートインターフェイスを作成して、プライベートインターフェイスがパブリックに公開されたプロパティをオーバーライドできるようにすることができます。つまり、プロパティをreadonlyからreadwriteに変更できます。
この使用例は、パブリックプロパティへのアクセスを制限するライブラリを作成するときに、同じプロパティがライブラリ内で完全な読み取り/書き込みアクセスを必要とする場合です。
Apple Docsリンク:https : //developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html
「クラス拡張を使用して個人情報を非表示にする」を検索します。
したがって、この手法はクラス拡張には有効ですが、カテゴリには有効ではありません。
カテゴリは良いことですが、乱用される可能性があります。カテゴリを作成するときは、原則として既存のメソッドを再実装しないでください。別のクラスが依存するコードを書き直しているため、これを行うと奇妙な副作用が発生する可能性があります。既知のクラスを壊して、デバッガを裏返しにしてしまう可能性があります。それは単に悪いプログラミングです。
それを行う必要がある場合は、実際にサブクラス化する必要があります。
それから、スウィズリングの提案、それは私にとって大きなNO-NO-NOです。
実行時にそれを入れ替えることは完全なNO-NO-NOです。
バナナをオレンジのように見せたいのですが、実行時のみですか?オレンジが欲しいなら、オレンジを書いてください。
バナナのように見せたり、オレンジのように振ったりしないでください。そしてさらに悪いことに、あなたのバナナを秘密のエージェントに変えないでください。秘密のエージェントは、オレンジをサポートするために世界中で静かにバナナを妨害します。
うわぁ!
super
それ以外の場合は呼び出すことができます。