web-dev-qa-db-ja.com

カテゴリを介してObjective Cのメソッドをオーバーライドする

以下はObjective Cで動作しています。

// Base Class in ClassA.h and ClassA.m
@interface ClassA : NSObject 
- (NSString *) myMethod;
@end
@implementation ClassA
- (NSString*) myMethod { return @"A"; }
@end

//Category in ClassA+CategoryB.h and ClassA+CategoryB.m
@interface ClassA (CategoryB) 
- (NSString *) myMethod;
@end
@implementation ClassA (CategoryB)
- (NSString*) myMethod { return @"B"; }
@end

問題は、ClassA.hをインポートしてメッセージを送信するだけの場合です。

[myClassA myMethod]; //returns B

なぜこれがBを返すのですか? ClassA + CategoryBをインポートしていません

私が次のことをした場合、さらに心配になります:

// Base Class in ClassA.h and ClassA.m
@interface ClassA : NSObject 
- (NSString *) myMethod;
- (NSString *) mySecondMethod;
@end
@implementation ClassA
- (NSString*) myMethod { return @"A"; }
- (NSString *) mySecondMethod { return [self myMethod]; }
@end

//Category in ClassA+CategoryB.h and ClassA+CategoryB.m
@interface ClassA (CategoryB) 
- (NSString *) myMethod;
@end
@implementation ClassA (CategoryB)
- (NSString*) myMethod { return @"B"; }
@end

そしてmySecondMethodを呼び出します:

ClassA *a = [[ClassA alloc] init];
NSLog(@"%@",[a myMethod]);

結果はBのままですが、(インポートがないため)カテゴリの実装について誰も知らないのです。

例外として、カテゴリをインポートしている場合にのみBを返します...

だからどんなヒントも感謝します。

20
Alexander

Objective-Cメッセージングは​​動的であるため、カテゴリをインポートするかどうかは関係ありません。オブジェクトはメッセージを受け取り、そのメソッドを実行します。

カテゴリがメソッドをオーバーライドしています。これは、ランタイムがそのオブジェクトにメッセージを送信するときに、何をインポートしても、オーバーライドされたメソッドを常に見つけることを意味します。

カテゴリを無視したい場合は、コンパイルしないでください。コンパイラのソースからカテゴリを削除できます。
代替はサブクラス化です。

これも読んでください:

カテゴリメソッド名の衝突を回避

カテゴリで宣言されたメソッドは既存のクラスに追加されるため、メソッド名には細心の注意を払う必要があります。

カテゴリで宣言されたメソッドの名前が元のクラスのメソッド、または同じクラス(またはスーパークラス)の別のカテゴリのメソッドと同じである場合、どのメソッドの実装が使用されるかについての動作は定義されていません。ランタイム。独自のクラスでカテゴリを使用している場合、これは問題になる可能性は低いですが、カテゴリを使用して標準のCocoaまたはCocoa Touchクラスにメソッドを追加するときに問題が発生する可能性があります。

したがって、あなたの場合、問題はありません。なぜなら、前述のように、これはユーザー定義のクラスでは起こりにくいからです。しかしカテゴリを記述する代わりに、サブクラスを使用する必要があります

40
Ramy Al Zuhouri

Obj-Cでは、カテゴリを使用して既存のクラスにメソッドを追加できます。したがって、NSStringにメソッドを追加すると、カテゴリ化されたメソッドは、NSMutableStringと、NSStringまたはNSStringのサブクラスを継承するすべてのクラスで使用できます。

ただし、カテゴリを上書きすることは避けてください。

どのメソッドが呼び出されるかは100%わかりません。コンパイラに依存します。

Appleドキュメントから。

現在、Objective-C言語では、カテゴリを使用して、クラスが継承するメソッド、またはクラスインターフェイスで宣言されたメソッドをオーバーライドすることができますが、そうすることはお勧めしません。カテゴリはサブクラスの代わりにはなりません。カテゴリを使用してメソッドをオーバーライドすることには、いくつかの重大な欠点があります。カテゴリが継承されたメソッドをオーバーライドすると、通常、カテゴリ内のメソッドは、スーパーへのメッセージを介して継承された実装を呼び出すことができます。ただし、カテゴリがそのカテゴリのクラスに存在するメソッドをオーバーライドする場合、元の実装を呼び出す方法はありません。カテゴリは、同じクラスの別のカテゴリで宣言されたメソッドを確実にオーバーライドすることはできません。 Cocoaクラスの多くはカテゴリを使用して実装されているため、この問題は特に重要です。オーバーライドしようとするフレームワーク定義のメソッド自体がカテゴリに実装されている可能性があるため、どの実装が優先されるかは定義されていません。一部のカテゴリメソッドが存在すると、すべてのフレームワークで動作が変化する可能性があります。たとえば、NSObjectのカテゴリでwindowWillClose:delegateメソッドをオーバーライドすると、プログラム内のすべてのウィンドウデリゲートがcategoryメソッドを使用して応答します。 NSWindowのすべてのインスタンスの動作が変わる可能性があります。フレームワーククラスに追加したカテゴリは、動作に不可解な変化を引き起こし、クラッシュを引き起こす可能性があります。

10
Anoop Vaidya

カテゴリで定義されたコードをコンパイルして暗黙的に含めました。

カテゴリコードの実行を回避する場合は、カテゴリ実装ファイルを削除して、ターゲットから削除する必要があります。あなたはそれを行うことができます

ターゲット->ビルドフェーズ->ソースのコンパイル

つまり、メソッドをオーバーライドするためにカテゴリを決して使用しないでください。これは非常に悪い習慣であり、それがカテゴリーの目的ではありません。

2