2009-09-10 3 views
24

私はソースを持たないObjective Cクラスのメソッドをオーバーライドしたいと思います。Objective CカテゴリでSuperを使用していますか?

私はそれを調べましたが、カテゴリでこれを可能にするはずですが、古いメソッドの結果を取得するためにスーパーメソッドを使用して、新しいメソッドで古いメソッドの結果を使用したいと思います。

私はこれを試してみると、私のメソッドが呼び出されますが、 "スーパー"は何もありません...任意のアイデアなぜですか?私はXCode 2.2 SDKを使ってiPhone開発をしています。私は間違いなくクラスのインスタンスを扱っています。クラスのメソッドはインスタンスメソッドです。

@implementation SampleClass (filePathResolver) 
-(NSString*) fullPathFromRelativePath:(NSString*) relPath 
{ 
    NSString *result = [super fullPathFromRelativePath: relPath]; 

    ... do some stuff with the old result 

    return result; 
} 

ノートと説明:Apple Docsで見ることができますが、これは許可されるべきですか?カテゴリには継承されたメソッドをオーバーライドする場合

Categories docs at developer.apple.com: は、 カテゴリの方法は、通常通り、スーパーへのメッセージを介して 継承実装を呼び出すことができます。ただし、カテゴリ が、カテゴリのクラスにすでに が存在するメソッドをオーバーライドすると、 は元の の実装を呼び出すことができません。

+0

あなたは今まで、この解決策を見つけますか?私はUILabelのsetText:メソッドに渡されるので、テキストパラメータを変更しようとしています。私はUILabel(巨大なコードベース)をサブクラス化したくないので、カテゴリーが最も適切かもしれません。 スーパーオブジェクトが実際にUILabelだったのであれば、super setTextを呼び出すことはおもしろいと思っていますが、そのカテゴリで定義されているsetText:を呼び出す無限ループにつながる可能性が非常に高いです。だから、これはおそらくカテゴリにとっては不可能な偉業であることは明らかです。 –

+0

ねえ!私はこのページに掲載されているもの以外の解決策は見つけられませんでした。メソッドswizzling、またはカテゴリー内の別のメソッドを使用する;-( –

答えて

28

カテゴリは元のクラスを拡張しますが、サブクラス化しないため、superを呼び出すとメソッドが見つかりません。

Method Swizzlingとします。しかし、あなたのコードが何かを破る可能性があることに注意してください。古いObjective-Cランタイムのメソッドスウィズルについての記事はTheocacao written by Scot Stevensonです。Cocoa with Love by Matt Gallagherには、新しいObjective-C 2.0ランタイムのメソッドスウィズルについての記事とその簡単な置き換えがあります。

また、クラスをサブクラス化してサブクラスを使用するか、+ (void)poseAsClass:(Class)aClassを使用してスーパークラスを置き換えることができます。 Appleは書いている:

ポーズクラス によって定義されたメソッドができ、superへのメッセージを通じて、 は、スーパークラスのメソッドを組み込むことが オーバーライドを。

Mac OS X 10.5では、Appleが非推奨のposeAsClass:を使用していることに注意してください。

+0

「Swizzling」というプログラミングコンセプトがある程度乱れていますが、それは面白い概念です –

+1

上記の説明を追加したように、アップルのドキュメントからの意味ですか? カテゴリが継承されたメソッドをオーバーライドすると、 カテゴリのメソッドは通常通り継承された実装をsuperにメッセージで呼び出すことができます。 –

+0

問題を解決する唯一の方法でない限り、メソッドスウィズルを望んでいないのですが、それでもスウィズルは脆弱性の集まりとなります – bbum

8

あなたはこのクラスに対してコーディングされた場合は、単にあなたのコードが使用できる何かにセレクタの名前を変更し、self上の元のセレクターを呼び出す:あなたは、このデフォルトの実装をオーバーライドしたい場合

@implementation SampleClass (filePathResolver) 
-(NSString*) myFullPathFromRelativePath:(NSString*) relPath 
{ 
    NSString *result = [self fullPathFromRelativePath: relPath]; 

    ... do some stuff with the old result 

    return result; 
} 

をそのクラスのセレクタを使用するには、method swizzlingアプローチを使用する必要があります。

+0

ええ、私はそのクラスに対して書かれたコードをすべて管理するわけではないので、それは私にとってはうまくいきませんが、提案に感謝します! –

+1

^ええ、そうです。メソッドswizzlingちょっとあなたのプログラムの巨大なハックと潜在的な将来のブレークポイントのように感じる - しかし、それはあなたがクラスに対して書かれたコードを制御していない正確にその状況で動作するはずです。 – n13

+0

よく私が意味するものは、そのクラスの "fullPathFromRelativePath"を呼び出すことができない他のコードがあります。私は他のコードが何をするかを変更することはできないので、この場合、うねりが私を助けません。 –

1

正確にはカテゴリではありませんが、実行時にメソッドを動的に追加することで回避策があります。彼の記事でサミュエルDéfagoがスーパーを呼び出すブロックIMPの実装を作成するためのきちんとした方法を説明し、彼の元の記事を見つけることができるhere

関連するコードは次のとおりです。

#import <objc/runtime.h> 
#import <objc/message.h> 

    const char *types = method_getTypeEncoding(class_getInstanceMethod(clazz, selector)); 
    class_addMethod(clazz, selector, imp_implementationWithBlock(^(__unsafe_unretained id self, va_list argp) { 
     struct objc_super super = { 
      .receiver = self, 
      .super_class = class_getSuperclass(clazz) 
     }; 

     id (*objc_msgSendSuper_typed)(struct objc_super *, SEL, va_list) = (void *)&objc_msgSendSuper; 
     return objc_msgSendSuper_typed(&super, selector, argp); 
    }), types); 
関連する問題