Pergunta

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

Estou especificamente tendo problemas de compreensão de como o código a seguir (de Cacau programação para Mac OS X, 3rd Edition ) funciona, mas, então, também ser capaz de aplicar os conceitos independentemente da amostra 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 recebo o que está tentando fazer. (BTW, employees é um NSArray de uma classe Person personalizada.)

Ser um cara NET, eu tento associar desconhecidas conceitos Obj-C e Cocoa a conceitos mais ou menos análogas .NET. É este semelhante ao conceito delegado da NET, mas sem tipo?

Isto não é 100% claro a partir do livro, então eu estou procurando algo suplementar de verdadeiros especialistas Cacau / Obj-C, novamente com a meta que eu entendo o conceito fundamental sob o exemplo simples (ish). Eu estou olhando realmente para ser capaz de aplicar de forma independente o conhecimento - até o capítulo 9, eu estava tendo dificuldade de fazer isso. Mas agora ...

Agradecemos antecipadamente!

Foi útil?

Solução

De acordo com a referência de classe NSInvocation da Apple :

Um NSInvocation é uma mensagem de Objective-C estática prestados, ou seja, é uma ação se transformou em um objeto.

E, em um pouco mais detalhes:

O conceito de mensagens é fundamental para a filosofia Objective-C. Toda vez que você chamar um método ou acessar uma variável de algum objeto, você está enviando uma mensagem. NSInvocation vem a calhar quando você quer enviar uma mensagem para um objeto em um ponto diferente no tempo, ou enviar a mesma mensagem várias vezes. NSInvocation permite que você descrever a mensagem que você está indo para enviar, em seguida, invoke -lo (na verdade enviá-lo para o objeto de destino) mais tarde.


Por exemplo, digamos que você deseja adicionar uma string para um array. Você normalmente enviar a mensagem addObject: da seguinte forma:

[myArray addObject:myString];

Agora, digamos que você quer usar NSInvocation para enviar esta mensagem em algum outro ponto no tempo:

Primeiro, você iria preparar um objeto NSInvocation para uso com o selector NSMutableArray de addObject::

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

Em seguida, você deve especificar qual objeto para enviar a mensagem para:

[myInvocation setTarget:myArray];

Especifique a mensagem que deseja enviar para esse objeto:

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

e preencha quaisquer argumentos para esse método:

[myInvocation setArgument:&myString atIndex:2];

Note que os argumentos objeto deve ser passado pelo ponteiro. Obrigado a Ryan McCuaig por apontar isso, e por favor, veja documentação da Apple para obter mais detalhes.

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

[myInvocation invoke];

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

Pense nisso como o envio de um e-mail. Você abrir um novo e-mail (objeto NSInvocation), preencha o endereço da pessoa (objeto) que você quer enviá-lo para, escreva uma mensagem para o destinatário (especificar um selector e argumentos) e clique em "Enviar" (invoke chamada).

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


usos NSUndoManager NSInvocation objetos para que ele possa reverter comandos. Essencialmente, o que você está fazendo é criar um objeto NSInvocation a dizer: "Ei, se você quiser desfazer o que eu fiz, envie esta mensagem a esse objeto, com estes argumentos". Você dá o objeto NSInvocation ao NSUndoManager, e acrescenta que objeto a uma série de ações que podem ser desfeitas. Se as chamadas do usuário "desfazer", NSUndoManager simplesmente olha para cima a ação mais recente na matriz, e invoca o objeto NSInvocation armazenados para executar a ação necessária.

Registrando Undo Operações para mais detalhes .

Outras dicas

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á:

  • Imprimir "Olá, mundo!"
  • Criar um NSMethodSignature por si.
  • Criar e preencher um NSInvocation, chamando a si mesmo.
  • Passe o NSInvocation a um NSTimer
  • O temporizador irá disparar em (aproximadamente) um segundo, fazendo com que o método a ser chamado novamente com seus argumentos originais.
  • Repetir.

No final, você vai ter 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!
...

É claro que o self objeto de destino deve continuar a existir para o NSTimer para enviar a NSInvocation a ele. Por exemplo, uma Singleton objeto, ou um AppDelegate que existe para a duração da aplicação.


UPDATE:

Como mencionado acima, quando você passar um NSInvocation como um argumento para um NSTimer, o NSTimer mantém automaticamente todos os argumentos do NSInvocation.

Se você não está passando um NSInvocation como um argumento para um NSTimer, e plano de tê-lo ficar por aqui por um tempo, você deve chamar seu método -retainArguments. Caso contrário, os seus argumentos podem ser desalocada antes da invocação é invocado, eventualmente causando o seu código de acidente. Aqui está como fazê-lo:

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 usar apenas a biblioteca aqui que é muito mais agradável: http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

Eu construir um exemplo simples de chamar vários tipos de métodos com NSInvocation.

Eu tive problemas durante a chamada vários parâmetros usando obj_msgSend

https://github.com/clearbrian/NSInvocation_Runtime

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top