2012-02-13 3 views
8

UPDATE維持しながら、各メソッドにコンテキスト認識コードを挿入:ジョージといくつかの重要な提案と、前後でAOP:DRY

を、私はには2つの異なる方法を作ってみました私はCodeRunnerに欲しいものを正確に達成し、GitHubのの要旨サイト上に掲載:それは新しい概念だと私はちょうど午前1:30で終了するので Objective-C AOP gist

コードが粗いです。それは間違いなく動作し、イニシャライザ、ゲッタまたはセッタではないすべてのメソッドを自動追加するようないくつかの細心の注意があります。 [END UPDATE]

私は、クラス内の各メソッドに対して状況依存コードを呼び出すことができる場合は、私のコードが少しDRYerになる状況に遭遇しました。 Objective-Cランタイムの使用はまったく問題ありません。私はCやC++のソリューションも受け入れます。

の代わりに:

- (void)methodName1 
{ 
    self->selector = _cmd; 
    NSLog(@"This method is named: %@",_cmd); 
    //more code 
} 

- (void)methodName2 
{ 
    self->selector = _cmd; 
    NSLog(@"This method is named: %@",_cmd); 
    //more code 
} 

が結果は同じであることで、このような何かを持っている:

+ (void)AOPMethod 
{ 
    self->selector = _cmd; 
    NSLog(@"This method is named: %@",_cmd); 
} 

- (void)methodName1 
{ 
    //more code 
} 

- (void)methodName2 
{ 
    //more code 
} 

を実際のアプリケーションでは、AOPMethodは、より多くのコードを含んでいるでしょうし、そこになるだろうクラス内のより多くのメソッド。

P.S.私はDRYにかなり執着しています。散文とパフォーマンスの明確さに加えて、それは長期的に私のコードの品質をどのように評価するかの重要な要素です。私が自分自身を繰り返すことを避けることができる新しい方法ごとに、多くのプロジェクトで共有されている再利用可能なクラスで可能な限り多くのコードを分解するので、利点は指数関数的です。

+0

入力/出力傍受を探していますか? –

+0

@GeorgFritzsche質問ありがとうございます。 メソッド代行受信がプラットフォームに依存しない場合、_cmdなどのメソッドレベルの状況依存データをクラスの各メソッド(コードの重複なし)で使用できるようにします。 言い換えれば、後者の例のいくつかのバージョンが前者と同じ結果を持つことができる、言及されている基本言語(すべてのプラットフォームで利用できないフレームワークではない)のいずれかで技術を共有できる場合、回答。再度、感謝します。 –

+0

あなたの特定のユースケース(同じ署名を持つすべてのメソッド)[このアプローチ](http://stackoverflow.com/questions/9242571/copy-a-method-imp-for-multiple-method-swizzles)メソッドリスト内のすべての適切なメソッドにパッチを適用するように拡張されました。現時点では、私はよりエレガントでより一般的な解決策を考えることはできません。 –

答えて

5

質問の具体的な使用例については、元の実装関数を置き換えるハンドラと、元の関数の前後の呼び出しを、this approachなどを使用して提供することができます。しかし、一般的には、実装されたメソッドシグネチャごとにハンドラ/インターセプトメソッドを提供する必要があるため、メソッド実装のパッチ適用は機能しません。

可変的な引数関数を除いてもっと一般的なものは、-forwardInvocation:を扱います。ここでの問題は、最初にそのメソッドを呼び出さなければならないということです。 ObjC2のメソッドを削除することはできないので、それを実行することはできません。

ただし、実行することができるのはforwardInvocation:を実装するプロキシを使用し、前後のハンドラを呼び出します。

@interface AspectProxy : NSProxy { 
    id target_; 
} 
- (id)initWithTarget:(id)target; 
@end 

@implementation AspectProxy 
- (id)initWithTarget:(id)target { 
    target_ = [target retain]; 
    return self; 
} 
- (void)dealloc { 
    [target_ release]; 
    [super dealloc]; 
} 
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { 
    return [target_ methodSignatureForSelector:sel]; 
} 
- (void)forwardInvocation:(NSInvocation *)inv { 
    SEL sel = [inv selector]; 
    NSLog(@"forwardInvocation for: %@", NSStringFromSelector(sel)); 
    if (sel == @selector(aspectBefore:) || sel == @selector(aspectAfter:)) { 
     return; 
    } 
    if ([target_ respondsToSelector:@selector(aspectBefore:)]) { 
     [target_ performSelector:@selector(aspectBefore:) withObject:inv]; 
    } 
    [inv invokeWithTarget:target_]; 
    if ([target_ respondsToSelector:@selector(aspectAfter:)]) { 
     [target_ performSelector:@selector(aspectAfter:) withObject:inv]; 
    } 
} 
@end 

我々はinit方法から実際のインスタンスを返す必要がないので、これでも透過的に行うことができる:

@interface Test : NSObject 
- (void)someFunction; 
@end 

@implementation Test 
- (id)init { 
    if (self = [super init]) { 
     return [[AspectProxy alloc] initWithTarget:[self autorelease]]; 
    } 
    return self; 
} 
- (void)aspectBefore:(NSInvocation *)inv { 
    NSLog(@"before %@", NSStringFromSelector([inv selector])); 
} 
- (void)aspectAfter:(NSInvocation *)inv { 
    NSLog(@"after %@", NSStringFromSelector([inv selector])); 
} 
- (void)someFunction { 
    NSLog(@"some function called"); 
} 
@end 

今、次のコード:

Test *x = [[[Test alloc] init] autorelease]; 
[x someFunction]; 

。 ..印刷されます:

forwardInvocationのために:someFunction
someFunction

と呼ばれるいくつかの関数の前にsomeFunction

後に実行可能なサンプルが this gistで見つけることができます

+1

これは本当にクールな解決策です。私はこのようなことを一度行ってデリゲートプロキシを作成し、オプションのメソッドに対して 'if([delegate respondsToSelector:]){}'と書く必要がなくなりました。よく働く! – Alex

+0

私は、forwardInvocationとNSProxyをオプションの1つとして使用してしまいましたが(質問を投稿した直後に知ったものですが)、ランタイムの使用にもっと夢中でしたので、これを行う方法も思いつきました。両方の方法は私の質問の要点にリンクされています。私はあなたのNSProxyバージョンがきれいなのが好きです。 –