NSInvocation for Dummies?

Como exatamente NSInvocation trabalha? Existe uma boa introdução?

Estou especificamente tendo problemas para entender como funciona o código a seguir (da programação de cacau para Mac OS X, 3ª edição ), mas também ser capaz de aplicar os conceitos independentemente da amostra do tutorial. O código:

 - (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]; } 

Eu entendo o que está tentando fazer. (BTW, os employees são um NSArray de uma class Person personalizada.)

Sendo um cara de .NET, eu tento associar conceitos desconhecidos de Obj-C e Cocoa a conceitos .NET mais ou menos análogos. Isso é semelhante ao conceito de delegado do .NET, mas não é typescript?

Isso não está 100% claro no livro, então estou procurando algo suplementar de especialistas em Cocoa / Obj-C, novamente com o objective de entender o conceito fundamental abaixo do exemplo simples (-ish). Eu estou realmente olhando para ser capaz de aplicar de forma independente o conhecimento – até o capítulo 9, eu não estava tendo dificuldade em fazer isso. Mas agora …

Desde já, obrigado!

De acordo com a referência da class NSInvocation da Apple :

Uma NSInvocation é uma mensagem Objective-C renderizada estática, ou seja, é uma ação transformada em um object.

E, um pouco mais detalhadamente:

O conceito de mensagens é central para a filosofia do objective-c. Toda vez que você chama um método, ou acessa uma variável de algum object, você está enviando uma mensagem. NSInvocation é útil quando você deseja enviar uma mensagem para um object em um ponto diferente no tempo ou enviar a mesma mensagem várias vezes. NSInvocation permite que você descreva a mensagem a ser enviada e, em seguida, invoque -a (na verdade, envie-a para o object de destino) mais tarde.


Por exemplo, digamos que você queira adicionar uma string a um array. Você normalmente enviaria a mensagem addObject: seguinte maneira:

 [myArray addObject:myString]; 

Agora, digamos que você queira usar NSInvocation para enviar esta mensagem em algum outro momento:

Primeiro, você prepararia um object NSInvocation para uso com o addObject: selector de NSMutableArray :

 NSMethodSignature * mySignature = [NSMutableArray instanceMethodSignatureForSelector:@selector(addObject:)]; NSInvocation * myInvocation = [NSInvocation invocationWithMethodSignature:mySignature]; 

Em seguida, você especificaria para qual object enviar a mensagem:

 [myInvocation setTarget:myArray]; 

Especifique a mensagem que você deseja enviar para esse object:

 [myInvocation setSelector:@selector(addObject:)]; 

E preencha todos os argumentos para esse método:

 [myInvocation setArgument:&myString atIndex:2]; 

Note que os argumentos do object devem ser passados ​​pelo ponteiro. Obrigado a Ryan McCuaig por apontar isso e, por favor, consulte a documentação da Apple para mais detalhes.

Neste ponto, myInvocation é um object completo, descrevendo uma mensagem que pode ser enviada. Para realmente enviar a mensagem, você ligaria:

 [myInvocation invoke]; 

Esta etapa final fará com que a mensagem seja enviada, essencialmente executando [myArray addObject:myString]; .

Pense nisso como enviar um email. Você abre um novo email (object NSInvocation ), preenche o endereço da pessoa (object) para quem deseja enviá-lo, digita uma mensagem para o destinatário (especifique um selector e argumentos) e clica em “enviar” ( invoke chamada).

Veja Usando NSInvocation para mais informações. Veja Usando NSInvocation se o acima não estiver funcionando.


NSUndoManager usa objects NSInvocation para que ele possa reverter os comandos. Essencialmente, o que você está fazendo é criar um object NSInvocation para dizer: “Ei, se você quiser desfazer o que acabei de fazer, envie essa mensagem para esse object, com esses argumentos”. Você fornece o object NSInvocation ao NSUndoManager e adiciona esse object a uma matriz de ações que podem ser desfeitas. Se o usuário chamar “Desfazer”, o NSUndoManager simplesmente procurará a ação mais recente na matriz e NSInvocation object NSInvocation armazenado para executar a ação necessária.

Consulte Registrando Operações de Desfazer para obter mais detalhes.

Aqui está um exemplo simples de NSInvocation em ação:

 - (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]; } 

Quando chamado – [self hello:@"Hello" world:@"world"]; – o método irá:

  • Impressão “Olá, mundo!”
  • Crie um NSMethodSignature para si mesmo.
  • Crie e preencha um NSInvocation, chamando a si mesmo.
  • Passe o NSInvocation para um NSTimer
  • O timer irá triggersr em (aproximadamente) 1 segundo, fazendo com que o método seja chamado novamente com seus argumentos originais.
  • Repetir.

No final, você receberá uma impressão assim:

 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! ... 

Obviamente, o self object alvo deve continuar existindo para que o NSTimer envie o NSInvocation para ele. Por exemplo, um object Singleton ou um AppDelegate que existe para a duração do aplicativo.


ATUALIZAR:

Como observado acima, quando você passa um NSInvocation como um argumento para um NSTimer, o NSTimer retém automaticamente todos os argumentos do NSInvocation.

Se você não está passando um NSInvocation como um argumento para um NSTimer, e planeja mantê-lo por um tempo, você deve chamar seu método -retainArguments . Caso contrário, seus argumentos podem ser desalocados antes que a chamada seja invocada, fazendo com que seu código falhe. Veja como fazer isso:

 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]; 

Você poderia tentar apenas usar a biblioteca aqui que é muito melhor: http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

Eu construo um exemplo simples de chamar vários tipos de methods com NSInvocation.

Eu tive problemas ao chamar vários params usando obj_msgSend

https://github.com/clearbrian/NSInvocation_Runtime