Pregunta

¿Cómo funciona exactamente NSInvocation ? ¿Hay una buena introducción?

Tengo problemas específicos para comprender cómo funciona el siguiente código (de Cocoa Programming for Mac OS X, 3rd Edition ), pero también puedo aplicar los conceptos independientemente del ejemplo del tutorial. El 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];
}

Entiendo lo que está tratando de hacer. (Por cierto, empleados es un NSArray de una clase personalizada Person ).

Siendo un tipo .NET, trato de asociar conceptos desconocidos de Obj-C y Cocoa con conceptos más o menos análogos .NET. ¿Es esto similar al concepto de delegado de .NET, pero sin tipo?

Esto no está 100% claro en el libro, por lo que estoy buscando algo complementario de expertos reales de Cocoa / Obj-C, nuevamente con el objetivo de que entiendo el concepto fundamental debajo del ejemplo simple (-ish). Realmente estoy buscando poder aplicar el conocimiento de forma independiente, hasta el capítulo 9, no tuve dificultades para hacerlo. Pero ahora ...

¡Gracias de antemano!

¿Fue útil?

Solución

De acuerdo con referencia de clase NSInvocation de Apple :

  

Un NSInvocation es un mensaje de Objective-C convertido en estático, es decir, es una acción convertida en un objeto.

Y, en un pequeño más detalle:

El concepto de mensajes es fundamental para la filosofía del objetivo-c. Cada vez que llama a un método o accede a una variable de algún objeto, le está enviando un mensaje. NSInvocation es útil cuando desea enviar un mensaje a un objeto en un momento diferente, o enviar el mismo mensaje varias veces. NSInvocation le permite describir el mensaje que va a enviar y luego invocarlo (en realidad, enviarlo al objeto de destino) más adelante.


Por ejemplo, supongamos que desea agregar una cadena a una matriz. Normalmente enviaría el mensaje addObject: de la siguiente manera:

[myArray addObject:myString];

Ahora, supongamos que desea utilizar NSInvocation para enviar este mensaje en otro momento:

Primero, prepararía un objeto NSInvocation para usar con el selector NSMutableArray addObject: selector:

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

A continuación, especificaría a qué objeto enviar el mensaje:

[myInvocation setTarget:myArray];

Especifique el mensaje que desea enviar a ese objeto:

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

Y complete cualquier argumento para ese método:

[myInvocation setArgument:&myString atIndex:2];

Tenga en cuenta que los argumentos de los objetos se deben pasar por puntero. Gracias a Ryan McCuaig por señalarlo, y consulte Documentación de Apple para más detalles.

En este punto, myInvocation es un objeto completo que describe un mensaje que se puede enviar. Para enviar el mensaje, debe llamar a:

[myInvocation invoke];

Este paso final hará que se envíe el mensaje, esencialmente ejecutando [myArray addObject: myString]; .

Piense en ello como enviar un correo electrónico. Abre un nuevo correo electrónico (objeto NSInvocation ), completa la dirección de la persona (objeto) a la que desea enviarlo, escribe un mensaje para el destinatario (especifique un selector y argumentos), y luego haga clic en "enviar" (llame a invoke ).

Consulte Uso de NSInvocation para obtener más información. Ver Uso de NSInvocation si lo anterior no funciona.


NSUndoManager usa objetos NSInvocation para que pueda invertir los comandos. Esencialmente, lo que está haciendo es crear un objeto NSInvocation para decir: "Hola, si desea deshacer lo que acabo de hacer, envíe este mensaje a ese objeto, con estos argumentos". Usted le da el objeto NSInvocation al NSUndoManager , y agrega ese objeto a una matriz de acciones imposibles de deshacer. Si el usuario invoca " Deshacer " ;, NSUndoManager simplemente busca la acción más reciente en la matriz e invoca el objeto NSInvocation almacenado para realizar la acción necesaria.

Consulte Registro de operaciones de deshacer para obtener más detalles .

Otros consejos

Aquí hay un ejemplo simple de NSInvocation en acción:

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

Cuando se llama - [self hello: @ " Hola " world: @ " world "]; : el método:

  • Imprimir " Hola mundo! "
  • Cree una NSMethodSignature para sí mismo.
  • Cree y complete una NSInvocation, llamándose a sí misma.
  • Pase la NSInvocation a un NSTimer
  • El temporizador se activará (aproximadamente) 1 segundo, haciendo que se vuelva a llamar al método con sus argumentos originales.
  • Repita.

Al final, obtendrá una copia impresa así:

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

Por supuesto, el objeto de destino self debe seguir existiendo para que el NSTimer le envíe la NSInvocation. Por ejemplo, un objeto Singleton , o un AppDelegate que existe mientras dura la aplicación.


UPDATE:

Como se señaló anteriormente, cuando pasa una NSInvocation como argumento a un NSTimer, el NSTimer retiene automáticamente todos los argumentos de NSInvocation.

Si no está pasando una NSInvocation como argumento a un NSTimer, y planea que se quede por un tiempo, debe llamar a su método -retainArguments . De lo contrario, sus argumentos pueden ser desasignados antes de que se invoque la invocación, lo que eventualmente ocasionará que su código se bloquee. Aquí se explica cómo hacerlo:

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

Podría intentar simplemente usar la biblioteca aquí, que es mucho mejor: http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

Construyo un ejemplo simple de llamar a varios tipos de métodos con NSInvocation.

Tuve problemas para llamar a varios parámetros usando obj_msgSend

https://github.com/clearbrian/NSInvocation_Runtime

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top