web-dev-qa-db-fra.com

NSInvocation pour les nuls?

Comment fonctionne exactement NSInvocation? Y a-t-il une bonne introduction?

Je rencontre spécifiquement des problèmes pour comprendre le fonctionnement du code suivant (de Cocoa Programming for Mac OS X, 3rd Edition), mais je peux également appliquer les concepts indépendamment de l'exemple de didacticiel. Le code:

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

J'obtiens ce qu'il essaie de faire. (BTW, employees est un NSArray d'une classe Person personnalisée.)

Étant un gars .NET, j'essaie d'associer des concepts Obj-C et Cocoa inconnus à des concepts .NET à peu près analogues. Est-ce similaire au concept de délégué de .NET, mais sans type?

Ce n'est pas clair à 100% dans le livre, donc je cherche quelque chose de supplémentaire de vrais experts de Cocoa/Obj-C, dans le but de comprendre le concept fondamental sous l'exemple simple (-ish). Je cherche vraiment à pouvoir appliquer les connaissances de manière indépendante - jusqu'au chapitre 9, je n'ai eu aucune difficulté à le faire. Mais maintenant ...

Merci d'avance!

136
John Rudy

Selon référence de classe NSInvocation d'Apple :

Un NSInvocation est un message Objective-C rendu statique, c'est-à-dire qu'il s'agit d'une action transformée en objet.

Et, dans un petit plus de détails:

Le concept de messages est au cœur de la philosophie objective-c. Chaque fois que vous appelez une méthode ou accédez à une variable d'un objet, vous lui envoyez un message. NSInvocation est pratique lorsque vous souhaitez envoyer un message à un objet à un moment différent ou envoyer le même message plusieurs fois. NSInvocation vous permet de décrire le message que vous allez envoyer, puis invoquer il (en fait l'envoyer à l'objet cible) plus tard.


Par exemple, supposons que vous souhaitiez ajouter une chaîne à un tableau. Vous enverriez normalement le addObject: message comme suit:

[myArray addObject:myString];

Supposons maintenant que vous souhaitiez utiliser NSInvocation pour envoyer ce message à un autre moment:

Tout d'abord, vous devez préparer un objet NSInvocation à utiliser avec NSMutableArray's addObject: sélecteur:

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

Ensuite, vous devez spécifier à quel objet envoyer le message:

[myInvocation setTarget:myArray];

Spécifiez le message que vous souhaitez envoyer à cet objet:

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

Et remplissez tous les arguments pour cette méthode:

[myInvocation setArgument:&myString atIndex:2];

Notez que les arguments d'objet doivent être passés par pointeur. Merci à Ryan McCuaig d'avoir signalé cela, et veuillez consulter documentation d'Apple pour plus de détails.

À ce stade, myInvocation est un objet complet, décrivant un message qui peut être envoyé. Pour envoyer réellement le message, vous devez appeler:

[myInvocation invoke];

Cette dernière étape entraînera l'envoi du message, essentiellement en exécutant [myArray addObject:myString];.

Pensez-y comme envoyer un e-mail. Vous ouvrez un nouvel e-mail (NSInvocation objet), saisissez l'adresse de la personne (objet) à qui vous souhaitez l'envoyer, tapez un message pour le destinataire (spécifiez un selector et arguments), puis cliquez sur "envoyer" (appelez invoke).

Voir tilisation de NSInvocation pour plus d'informations. Voir tilisation de NSInvocation si ce qui précède ne fonctionne pas.


NSUndoManager utilise les objets NSInvocation pour qu'il puisse inverser les commandes . Essentiellement, vous créez un objet NSInvocation pour dire: "Hé, si vous voulez annuler ce que je viens de faire, envoyez ce message à cet objet, avec ces arguments". Vous donnez l'objet NSInvocation au NSUndoManager et il ajoute cet objet à un tableau d'actions annulables. Si l'utilisateur appelle "Annuler", NSUndoManager recherche simplement l'action la plus récente dans le tableau et appelle l'objet NSInvocation stocké pour effectuer l'action nécessaire.

Voir Enregistrement des opérations d'annulation pour plus de détails.

278
e.James

Voici un exemple simple de NSInvocation en action:

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

Lorsque appelé - [self hello:@"Hello" world:@"world"]; - la méthode:

  • Imprimer "Bonjour tout le monde!"
  • Créez un NSMethodSignature pour lui-même.
  • Créez et remplissez une NSInvocation, en l'appelant.
  • Passez NSInvocation à un NSTimer
  • Le temporisateur se déclenche dans (environ) 1 seconde, provoquant à nouveau la méthode avec ses arguments d'origine.
  • Répéter.

À la fin, vous obtiendrez une impression comme ceci:

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

Bien sûr, l'objet cible self doit continuer d'exister pour que NSTimer puisse lui envoyer NSInvocation. Par exemple, un objet Singleton ou un AppDelegate qui existe pour la durée de l'application.


MISE À JOUR:

Comme indiqué ci-dessus, lorsque vous transmettez un NSInvocation en tant qu'argument à un NSTimer, le NSTimer conserve automatiquement tous les arguments de NSInvocation.

Si vous ne passez pas un NSInvocation comme argument à un NSTimer et prévoyez de le faire rester pendant un certain temps, vous devez appeler son -retainArguments méthode. Sinon, ses arguments peuvent être désalloués avant que l'invocation ne soit invoquée, entraînant éventuellement le blocage de votre code. Voici comment procéder:

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];
46
Dave Gallagher

Vous pouvez essayer simplement d'utiliser la bibliothèque ici qui est beaucoup plus agréable: http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

5
Casebash

Je construis un exemple simple d'appel de divers types de méthodes avec NSInvocation.

J'ai eu des problèmes pour appeler plusieurs paramètres avec obj_msgSend

https://github.com/clearbrian/NSInvocation_Runtime

0
brian.clear