NSInvocation
はどのように機能しますか?良い紹介はありますか?
具体的には、次のコード(Mac OS X向けCocoaプログラミング、第3版)の動作を理解するのに問題がありますが、チュートリアルサンプルとは別に概念を適用することもできます。コード:
- (void)insertObject:(Person *)p inEmployeesAtIndex:(int)index
{
NSLog(@"adding %@ to %@", p, employees);
// Add inverse of this operation to undo stack
NSUndoManager *undo = [self undoManager];
[[undo prepareWithInvocationTarget:self] removeObjectFromEmployeesAtIndex:index];
if (![undo isUndoing])
[undo setActionName:@"Insert Person"];
// Finally, add person to the array
[employees insertObject:p atIndex:index];
}
- (void)removeObjectFromEmployeesAtIndex:(int)index
{
Person *p = [employees objectAtIndex:index];
NSLog(@"removing %@ from %@", p, employees);
// Add inverse of this operation to undo stack
NSUndoManager *undo = [self undoManager];
[[undo prepareWithInvocationTarget:self] insertObject:p
inEmployeesAtIndex:index];
if (![undo isUndoing])
[undo setActionName:@"Delete Person"];
// Finally, remove person from array
[employees removeObjectAtIndex:index];
}
私はそれがやろうとしていることを理解しています。 (ところで、employees
は、カスタムNSArray
クラスのPerson
です。)
.NETの人間なので、なじみのないObj-CとCocoaの概念をおおまかに類似した.NETの概念に関連付けようとします。これは.NETのデリゲートコンセプトに似ていますが、型付けされていませんか?
これは本から100%明確ではないので、単純な(-ish)例の下にある基本概念を理解するという目標で、本物のCocoa/Obj-C専門家から補足的なものを探しています。私は本当に知識を独立して適用できるようにしたいと考えています-第9章まで、私はそれをするのに苦労していませんでした。でも今 ...
前もって感謝します!
AppleのNSInvocationクラス参照 によると:
NSInvocation
は静的にレンダリングされたObjective-Cメッセージです。つまり、オブジェクトに変換されるアクションです。
そして、littleより詳細に:
メッセージの概念は、objective-c哲学の中心です。メソッドを呼び出したり、オブジェクトの変数にアクセスしたりするたびに、メッセージを送信しています。 NSInvocation
は、異なる時点でオブジェクトにメッセージを送信する場合、または同じメッセージを複数回送信する場合に役立ちます。 NSInvocation
を使用すると、送信するメッセージをdescribe、次にinvoke後で(実際にターゲットオブジェクトに送信します)。
たとえば、文字列を配列に追加するとします。通常、次のようにaddObject:
メッセージを送信します。
[myArray addObject:myString];
ここで、NSInvocation
を使用して、他のある時点でこのメッセージを送信するとします。
最初に、NSInvocation
のaddObject:
セレクターで使用するNSMutableArray
オブジェクトを準備します。
NSMethodSignature * mySignature = [NSMutableArray
instanceMethodSignatureForSelector:@selector(addObject:)];
NSInvocation * myInvocation = [NSInvocation
invocationWithMethodSignature:mySignature];
次に、メッセージを送信するオブジェクトを指定します。
[myInvocation setTarget:myArray];
そのオブジェクトに送信するメッセージを指定します。
[myInvocation setSelector:@selector(addObject:)];
そして、そのメソッドの引数を入力します:
[myInvocation setArgument:&myString atIndex:2];
オブジェクトの引数はポインタで渡す必要があることに注意してください。 Ryan McCuaig に感謝します。詳細については Appleのドキュメント をご覧ください。
この時点で、myInvocation
は完全なオブジェクトであり、送信可能なメッセージを記述しています。実際にメッセージを送信するには、次を呼び出します。
[myInvocation invoke];
この最後の手順により、メッセージが送信され、基本的に[myArray addObject:myString];
が実行されます。
メールを送信するようなものだと考えてください。新しい電子メール(NSInvocation
オブジェクト)を開き、送信先の人(オブジェクト)のアドレスを入力し、受信者へのメッセージを入力します(selector
を指定します)および引数)、[送信]をクリックします(invoke
を呼び出します)。
詳細については、 NSInvocationの使用 を参照してください。上記が機能しない場合は、 NSInvocationの使用 を参照してください。
NSUndoManager
はNSInvocation
オブジェクトを使用して、コマンドをreverseできるようにします。基本的に、あなたがしていることは、NSInvocation
オブジェクトを作成することです。「ねえ、あなたが今やったことを元に戻したいなら、これらの引数を使ってこのメッセージをそのオブジェクトに送信してください」 NSInvocation
オブジェクトをNSUndoManager
に渡すと、そのオブジェクトが取り消し可能なアクションの配列に追加されます。ユーザーが「元に戻す」を呼び出すと、NSUndoManager
は配列内の最新のアクションを検索し、保存されたNSInvocation
オブジェクトを呼び出して必要なアクションを実行します。
詳細については、「 ndo操作の登録 」を参照してください。
NSInvocationの実際の簡単な例を次に示します。
- (void)hello:(NSString *)hello world:(NSString *)world
{
NSLog(@"%@ %@!", hello, world);
NSMethodSignature *signature = [self methodSignatureForSelector:_cmd];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self]; // index 0 (hidden)
[invocation setSelector:_cmd]; // index 1 (hidden)
[invocation setArgument:&hello atIndex:2]; // index 2
[invocation setArgument:&world atIndex:3]; // index 3
// NSTimer's always retain invocation arguments due to their firing delay. Release will occur when the timer invalidates itself.
[NSTimer scheduledTimerWithTimeInterval:1 invocation:invocation repeats:NO];
}
呼び出されたとき-[self hello:@"Hello" world:@"world"];
-メソッドは以下を行います:
最終的に、次のような印刷結果が得られます。
2010-07-11 17:48:45.262 Your App[2523:a0f] Hello world!
2010-07-11 17:48:46.266 Your App[2523:a0f] Hello world!
2010-07-11 17:48:47.266 Your App[2523:a0f] Hello world!
2010-07-11 17:48:48.267 Your App[2523:a0f] Hello world!
2010-07-11 17:48:49.268 Your App[2523:a0f] Hello world!
2010-07-11 17:48:50.268 Your App[2523:a0f] Hello world!
2010-07-11 17:48:51.269 Your App[2523:a0f] Hello world!
...
もちろん、NSTimerがNSInvocationを送信するには、ターゲットオブジェクトself
が存在し続ける必要があります。たとえば、シングルトンオブジェクト、またはアプリケーションの期間中に存在するAppDelegate。
更新:
上記のように、NSInvocationを引数としてNSTimerに渡すと、NSTimerはNSInvocationのすべての引数を自動的に保持します。
NSInvocationをNSTimerの引数として渡さず、しばらくそれを使い続ける予定の場合は、-retainArguments
メソッドを呼び出す必要があります。そうしないと、呼び出しが呼び出される前に引数の割り当てが解除され、最終的にコードがクラッシュする可能性があります。方法は次のとおりです。
NSMethodSignature *signature = ...;
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
id arg1 = ...;
id arg2 = ...;
[invocation setTarget:...];
[invocation setSelector:...];
[invocation setArgument:&arg1 atIndex:2];
[invocation setArgument:&arg2 atIndex:3];
[invocation retainArguments]; // If you do not call this, arg1 and arg2 might be deallocated.
[self someMethodThatInvokesYourInvocationEventually:invocation];
ここにあるライブラリを使用するだけでいいでしょう: http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html
NSInvocationを使用してさまざまなメソッドタイプを呼び出す簡単な例を作成します。
Obj_msgSendを使用して複数のパラメーターを呼び出すときに問題が発生しました