NSInvocation для чайников? - PullRequest
       22

NSInvocation для чайников?

136 голосов
/ 24 ноября 2008

Как именно NSInvocation работает? Есть хорошее введение?

У меня, в частности, возникают проблемы с пониманием того, как работает следующий код (из Программирование какао для Mac OS X, 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% ясно из книги, поэтому я ищу что-то дополнительное от настоящих экспертов по какао / Obj-C, опять же с целью, чтобы я понял фундаментальную концепцию под простым (-ым) примером. Я действительно надеюсь, что смогу применить полученные знания самостоятельно - вплоть до 9-й главы у меня не было трудностей с этим. Но сейчас ...

Заранее спасибо!

Ответы [ 4 ]

278 голосов
/ 24 ноября 2008

Согласно Ссылка Apple на класс NSInvocation :

NSInvocation - это сообщение Objective C, отображаемое как статическое, то есть это действие, превращенное в объект.

И, в немного более подробно:

Концепция сообщений является центральной в философии объективной с. Каждый раз, когда вы вызываете метод или обращаетесь к переменной какого-либо объекта, вы отправляете ему сообщение. NSInvocation пригодится, если вы хотите отправить сообщение объекту в другой момент времени или отправить одно и то же сообщение несколько раз. NSInvocation позволяет вам описать сообщение, которое вы собираетесь отправить, а затем вызвать его (фактически отправить его целевому объекту) позже.


Например, допустим, вы хотите добавить строку в массив. Обычно вы отправляете сообщение addObject: следующим образом:

[myArray addObject:myString];

Теперь предположим, что вы хотите использовать NSInvocation для отправки этого сообщения в другой момент времени:

Сначала вы должны подготовить объект NSInvocation для использования с селектором NSMutableArray addObject::

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, и он добавляет этот объект в массив отменяемых действий. Если пользователь вызывает «Undo», NSUndoManager просто ищет самое последнее действие в массиве и вызывает сохраненный объект NSInvocation для выполнения необходимого действия.

Подробнее см. Регистрация операций отмены .

46 голосов
/ 12 июля 2010

Вот простой пример 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!
...

Конечно, целевой объект self должен продолжать существовать, чтобы NSTimer отправлял ему NSInvocation. Например, объект Singleton или AppDelegate, который существует на время действия приложения.


UPDATE:

Как отмечено выше, когда вы передаете 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];
5 голосов
/ 02 февраля 2010

Вы можете попробовать использовать библиотеку, которая гораздо лучше: http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

0 голосов
/ 28 мая 2014

Я строю простой пример вызова различных типов методов с помощью NSInvocation.

У меня были проблемы с вызовом нескольких параметров с использованием obj_msgSend

https://github.com/clearbrian/NSInvocation_Runtime

...