NSInvocation for Dummies?


139

정확히 어떻게 NSInvocation작동합니까? 좋은 소개가 있습니까?

특히 다음 코드 ( Mac OS X 용 Cocoa Programming, 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];
}

내가하려는 일을 얻습니다. (BTW, employeesNSArray사용자 정의의 Person클래스입니다.)

.NET 사용자이기 때문에 익숙하지 않은 Obj-C 및 Cocoa 개념을 대략 비슷한 .NET 개념과 연결하려고합니다. 이것은 .NET의 대리자 개념과 비슷하지만 유형이 지정되지 않았습니까?

이것은 책에서 100 % 명확하지 않으므로 간단한 Cocoa / Obj-C 전문가의 보충 자료를 찾고 있습니다. 단순한 (-ish) 예제 아래의 기본 개념을 이해한다는 목표로 다시합니다. 나는 지식을 독립적으로 적용 할 수있는 방법을 찾고 있습니다. 9 장까지는 그렇게하는 데 어려움이 없었습니다. 그러나 지금 ...

미리 감사드립니다!

답변:


284

Apple의 NSInvocation 클래스 참조 에 따르면 :

NSInvocation오브젝티브 C 메시지 렌더링 정적, 즉, 그 물체로 전환 동작이다.

그리고 더 자세하게 :

메시지의 개념은 objective-c 철학의 핵심입니다. 메소드를 호출하거나 일부 객체의 변수에 액세스 할 때마다 메시지를 보냅니다. NSInvocation다른 시점에 객체에 메시지를 보내거나 동일한 메시지를 여러 번 보내려고 할 때 편리합니다. 보내려는 메시지 NSInvocation설명 하고 나중에 메시지를 호출 (실제로 대상 객체로 전송) 할 수 있습니다.


예를 들어 배열에 문자열을 추가한다고 가정 해 봅시다. 일반적 addObject:으로 다음과 같이 메시지를 보냅니다 .

[myArray addObject:myString];

이제 NSInvocation다른 시점에서이 메시지를 보내는 데 사용한다고 가정 해 보겠습니다 .

먼저 의 선택기 NSInvocation와 함께 사용할 객체를 준비합니다 .NSMutableArrayaddObject:

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되돌릴 수 있도록 객체를 사용 합니다. 본질적으로, 당신이하고있는 일은 NSInvocation"내가 방금 한 것을 취소하고 싶다면,이 메시지를 그 인수들과 함께 그 객체로 보내라"는 말을 하는 객체를 만드는 것입니다. NSInvocation객체를에 제공하면 NSUndoManager해당 객체를 실행 취소 가능한 작업 배열에 추가합니다. 사용자가 "실행 취소"를 호출 NSUndoManager하면 배열에서 가장 최근의 동작을 찾아 저장된 NSInvocation객체를 호출 하여 필요한 동작을 수행합니다.

자세한 내용은 실행 취소 작업 등록 을 참조하십시오.


10
다른 훌륭한 대답에 대한 하나의 사소한 수정 ...에 객체에 대한 포인터를 전달 setArgument:atIndex:해야하므로 arg 할당이 실제로 읽혀 야 [myInvocation setArgument:&myString atIndex:2]합니다.
Ryan McCuaig 2009

60
Ryan의 설명을 명확히하기 위해 인덱스 0은 "self"로 예약되고 인덱스 1은 "_cmd"로 예약되어 있습니다 (자세한 내용은 e.James 링크 참조). 따라서 첫 번째 인수는 인덱스 2에, 두 번째 인수는 인덱스 3에 배치됩니다.
Dave

4
@haroldcampbell : 우리는 무엇을 불러야합니까?
e.James

6
mySignature에 선택기를 이미 지정 했으므로 setSelector를 호출해야하는 이유를 이해할 수 없습니다.
Gleno

6
@Gleno : NSInvocation은 매우 유연합니다. 메소드 서명과 일치하는 선택기를 실제로 설정할 수 있으므로 메소드 서명을 작성하는 데 사용 된 선택기를 반드시 사용할 필요는 없습니다. 이 예제에서 setSelector : @selector (removeObject :)는 동일한 메소드 서명을 공유하므로 쉽게 수행 할 수 있습니다.
e.James

48

다음은 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!
...

물론 selfNSTimer가 NSInvocation을 보내려면 대상 객체 가 계속 존재해야합니다. 예를 들어 응용 프로그램 기간 동안 존재 하는 Singleton 개체 또는 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];

6
invocationWithMethodSignature:이니셜 라이저를 사용하더라도을 호출해야 한다는 것은 흥미 롭습니다 setSelector:. 중복 된 것처럼 보이지만 방금 테스트했으며 필요합니다.
ThomasW

이것이 무한 루프로 계속 실행됩니까? 그리고 _cmd는 무엇입니까
j2emanue 16:28에


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.