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!
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.
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:
À 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];
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
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