Réponses:
Fondamentalement, 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.
Ainsi, même si ceux-ci sont équivalents:
[anObject aMethod];
[anObject performSelector:@selector(aMethod)];
Le deuxième formulaire vous permet de faire ceci:
SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];
avant d'envoyer le message.
performSelector:
est quelque chose que vous ne faites probablement que si vous implémentez l'action cible dans votre classe. Les frères performSelectorInBackground:withObject:
et sœurs performSelectorOnMainThread:withObject:waitUntilDone:
sont souvent plus utiles. Pour générer un thread d'arrière-plan et pour rappeler les résultats au thread principal à partir dudit thread d'arrière-plan.
performSelector
est également utile pour supprimer les avertissements de compilation. Si vous savez que la méthode existe (comme après l'utilisation respondsToSelector
), cela empêchera Xcode de dire "peut ne pas répondre à your_selector
". Ne l'utilisez pas au lieu de découvrir la véritable cause de l'avertissement. ;)
Pour cet exemple très basique dans la question,
[object doSomething];
[object performSelector:@selector(doSomething)];
il n'y a aucune différence dans ce qui va se passer. doSomething 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, parce que [object doSomethingWithMyAge: 42];
ne peut plus être appelée avec aucune variante de "performSelector", car toutes les variantes avec paramètres n'acceptent que les paramètres d'objet.
Le sélecteur ici serait "doSomethingWithMyAge:" mais toute tentative de
[object performSelector:@selector(doSomethingWithMyAge:) withObject:42];
ne compile tout simplement pas. passer un NSNumber: @ (42) au lieu de 42, n'aiderait pas non plus, car la méthode attend un type C de base - 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 des 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 renvoyer un simple BOOL ou NSUInteger aussi, et cela a fonctionné.
L'une des deux principales utilisations de performSelector est de composer dynamiquement le nom de la méthode que vous souhaitez 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 est d'envoyer de manière asynchrone un message à l'objet, qui sera exécuté plus tard sur la boucle d'exécution actuelle. 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és à partir de plusieurs catégories de classes Foundation, comme NSThread, NSRunLoop et NSObject)
Chacune des variantes a son propre comportement spécial, mais toutes partagent quelque chose en commun (au moins lorsque waitUntilDone est défini sur NO). L'appel "performSelector" reviendrait immédiatement, et le message à l'objet ne sera placé sur la boucle d'exécution actuelle qu'après un certain temps.
En raison de l'exécution retardée - naturellement aucune valeur de retour n'est disponible dans la méthode du sélecteur, d'où la valeur de retour - (void) dans toutes ces variantes asynchrones.
J'espère avoir couvert cela d'une manière ou d'une autre ...
@ennuikiller est parfait. Fondamentalement, les sélecteurs générés dynamiquement sont utiles lorsque vous ne connaissez pas (et ne pouvez généralement pas) connaître le nom de la méthode que vous appellerez lorsque vous compilerez le code.
Une différence clé est que -performSelector:
et les amis (y compris les variantes multi-thread et retardé ) 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 0-2. Par exemple, appeler -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:
avec 6 paramètres et renvoyer le NSString
est assez compliqué et n'est pas pris en charge par les méthodes fournies.
NSInvocation
objet.
performSelector:
et les amis prennent tous des arguments d'objet, ce qui signifie que vous ne pouvez pas les utiliser pour appeler (par exemple) setAlphaValue:
, car son argument est un flottant.
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. De plus, comme les pointeurs de fonction, ils encapsulent uniquement la partie verbale de l'invocation. Si la méthode a des paramètres, vous devrez également les transmettre.
An NSInvocation
sert un objectif similaire, sauf qu'il rassemble plus d'informations. Non seulement il inclut la partie verbe, mais il inclut é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 créer un fichier approprié NSInvocation
et le déclencher plus tard.
Il y a une autre différence subtile entre les deux.
[object doSomething]; // is executed right away
[object performSelector:@selector(doSomething)]; // gets executed at the next runloop
Voici l'extrait de la documentation Apple
"performSelector: withObject: afterDelay: exécute le sélecteur spécifié sur le thread actuel lors du prochain cycle de boucle d'exécution et après une période de délai facultative. Comme il attend le prochain cycle de boucle d'exécution pour exécuter le sélecteur, ces méthodes fournissent un mini-délai automatique de le code en cours d'exécution. Plusieurs sélecteurs en file d'attente sont exécutés l'un après l'autre dans l'ordre dans lequel ils ont été mis en file d'attente. "
performSelector:withObject:afterDelay:
, mais la question et votre extrait de code utilisent performSelector:
, ce qui est une méthode entièrement différente. À partir de la documentation pour cela: <quote> La performSelector:
méthode équivaut à envoyer un aSelector
message directement au destinataire. </quote>
performSelector/performSelector:withObject/performSelector:withObject:afterDelay
tous se comportaient de la même manière, ce qui était une erreur.