web-dev-qa-db-ja.com

NSInvocation for Dummies?

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章まで、私はそれをするのに苦労していませんでした。でも今 ...

前もって感謝します!

136
John Rudy

AppleのNSInvocationクラス参照 によると:

NSInvocationは静的にレンダリングされたObjective-Cメッセージです。つまり、オブジェクトに変換されるアクションです。

そして、littleより詳細に:

メッセージの概念は、objective-c哲学の中心です。メソッドを呼び出したり、オブジェクトの変数にアクセスしたりするたびに、メッセージを送信しています。 NSInvocationは、異なる時点でオブジェクトにメッセージを送信する場合、または同じメッセージを複数回送信する場合に役立ちます。 NSInvocationを使用すると、送信するメッセージをdescribe、次にinvoke後で(実際にターゲットオブジェクトに送信します)。


たとえば、文字列を配列に追加するとします。通常、次のようにaddObject:メッセージを送信します。

[myArray addObject:myString];

ここで、NSInvocationを使用して、他のある時点でこのメッセージを送信するとします。

最初に、NSInvocationaddObject:セレクターで使用する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の使用 を参照してください。


NSUndoManagerNSInvocationオブジェクトを使用して、コマンドをreverseできるようにします。基本的に、あなたがしていることは、NSInvocationオブジェクトを作成することです。「ねえ、あなたが今やったことを元に戻したいなら、これらの引数を使ってこのメッセージをそのオブジェクトに送信してください」 NSInvocationオブジェクトをNSUndoManagerに渡すと、そのオブジェクトが取り消し可能なアクションの配列に追加されます。ユーザーが「元に戻す」を呼び出すと、NSUndoManagerは配列内の最新のアクションを検索し、保存されたNSInvocationオブジェクトを呼び出して必要なアクションを実行します。

詳細については、「 ndo操作の登録 」を参照してください。

278
e.James

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"];-メソッドは以下を行います:

  • 「Hello world!」を印刷
  • NSMethodSignatureを作成します。
  • NSInvocationを作成して入力し、それ自体を呼び出します。
  • NSInvocationをNSTimerに渡します
  • タイマーは(約)1秒で起動し、元の引数でメソッドが再度呼び出されます。
  • 繰り返す。

最終的に、次のような印刷結果が得られます。

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];
46
Dave Gallagher

ここにあるライブラリを使用するだけでいいでしょう: http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

5
Casebash

NSInvocationを使用してさまざまなメソッドタイプを呼び出す簡単な例を作成します。

Obj_msgSendを使用して複数のパラメーターを呼び出すときに問題が発生しました

https://github.com/clearbrian/NSInvocation_Runtime

0
brian.clear