NSInvocation for Dummies?
-
10-07-2019 - |
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!
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
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