web-dev-qa-db-fra.com

Utiliser -performSelector: vs appeler simplement la méthode

Je suis toujours un peu nouveau dans Objective-C et je me demande quelle est la différence entre les deux déclarations suivantes?

[object performSelector:@selector(doSomething)]; 

[object doSomething];
111
TheGambler

En gros, performSelector vous permet de déterminer dynamiquement quel sélecteur appeler un sélecteur sur l'objet donné. En d'autres termes, le sélecteur n'a pas besoin d'être déterminé avant l'exécution.

Donc même si ceux-ci sont équivalents:

[anObject aMethod]; 
[anObject performSelector:@selector(aMethod)]; 

La deuxième forme vous permet de faire ceci:

SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];

avant d'envoyer le message.

189
ennuikiller

Pour cet exemple très basique dans la question,

[object doSomething];
[object performSelector:@selector(doSomething)]; 

il n'y a pas de différence dans ce qui va se passer. doQuelque chose sera exécuté de manière synchrone par objet. Seul "doSomething" est une méthode très simple, qui ne renvoie rien et ne nécessite aucun paramètre.

était-ce quelque chose d'un peu plus compliqué, comme:

(void)doSomethingWithMyAge:(NSUInteger)age;

les choses se compliqueraient, car [object doSomethingWithMyAge: 42];

ne peut plus être appelé avec aucune variante de "performSelector", car toutes les variantes avec paramètres acceptent uniquement les paramètres d'objet.

Le sélecteur ici serait "doSomethingWithMyAge:" mais toute tentative de

[object performSelector:@selector(doSomethingWithMyAge:) withObject:42];  

ne veut tout simplement pas compiler. passer un NSNumber: @ (42) au lieu de 42 ne servirait à rien non plus, car la méthode attend un type C basique - pas un objet.

De plus, il existe des variantes performSelector jusqu'à 2 paramètres, pas plus. Alors que les méthodes ont souvent beaucoup plus de paramètres.

J'ai découvert que bien que les variantes synchrones de performSelector:

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

toujours retourner un objet, j'ai pu retourner un simple BOOL ou NSUInteger aussi, et cela a fonctionné.

Une des deux utilisations principales de performSelector est de composer dynamiquement le nom de la méthode à exécuter, comme expliqué dans une réponse précédente. Par exemple

 SEL method = NSSelectorFromString([NSString stringWithFormat:@"doSomethingWithMy%@:", @"Age");
[object performSelector:method];

L’autre utilisation consiste à envoyer de manière asynchrone un message à un objet, qui sera exécuté ultérieurement sur le cycle d’exécution en cours. Pour cela, il existe plusieurs autres variantes de performSelector.

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
- (void)performSelector:(SEL)aSelector target:(id)target argument:(id)arg order:(NSUInteger)order modes:(NSArray *)modes;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;

(oui, je les ai rassemblées dans plusieurs catégories de la classe Foundation, comme NSThread, NSRunLoop et NSObject)

Chacune des variantes a son propre comportement, mais toutes partagent un point commun (du moins lorsque waitUntilDone est défini sur NO). L'appel "performSelector" est renvoyé immédiatement et le message à l'objet ne sera placé dans le cycle actuel qu'après un certain temps.

En raison de l'exécution retardée - aucune valeur de retour n'est naturellement disponible par rapport à la méthode du sélecteur, d'où la valeur de retour - (void) dans toutes ces variantes asynchrones.

J'espère que j'ai couvert cela en quelque sorte ...

13
Motti Shneor

@ennuikiller est sur place. Fondamentalement, les sélecteurs générés dynamiquement sont utiles lorsque vous ne connaissez pas (et qu’il est généralement impossible) le nom de la méthode que vous appelez lors de la compilation du code.

Une différence clé est que -performSelector: et amis (y compris les variantes multi-threadées et retardées ) sont quelque peu limités en ce sens qu’ils sont conçus pour être utilisés avec des méthodes avec des paramètres de 0 à 2. Par exemple, appelez -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation: avec 6 paramètres et renvoyer le NSString est assez lourd et n'est pas supporté par les méthodes fournies.

11
Quinn Taylor

Les sélecteurs sont un peu comme des pointeurs de fonction dans d'autres langues. Vous les utilisez lorsque vous ne savez pas au moment de la compilation quelle méthode vous souhaitez appeler au moment de l'exécution. En outre, comme les pointeurs de fonction, ils encapsulent uniquement la partie verbe de l'invocation. Si la méthode a des paramètres, vous devrez également les transmettre.

Un NSInvocation a un but similaire, sauf qu'il rassemble plus d'informations. Non seulement il inclut la partie verbale, il comprend également l’objet cible et les paramètres. Ceci est utile lorsque vous souhaitez appeler une méthode sur un objet particulier avec des paramètres particuliers, pas maintenant, mais dans le futur. Vous pouvez construire un NSInvocation approprié et le déclencher plus tard.

3
Daniel Yankowsky