Comment créer dynamiquement un sélecteur lors de l'exécution avec Objective-C?

93

Je sais comment créer un SELau moment de la compilation en utilisant @selector(MyMethodName:)mais ce que je veux faire est de créer un sélecteur dynamiquement à partir d'un fichier NSString. Est-ce seulement possible?

Qu'est-ce que je peux faire:

SEL selector = @selector(doWork:);
[myobj respondsToSelector:selector];

Ce que je veux faire: (pseudo code, cela ne fonctionne évidemment pas)

SEL selector = selectorFromString(@"doWork");
[myobj respondsToSelector:selector];

J'ai cherché dans la documentation de l'API Apple, mais je n'ai pas trouvé de moyen qui ne repose pas sur la @selector(myTarget:)syntaxe de compilation .

Craigb
la source

Réponses:

180

Je ne suis pas un programmeur Objective-C, simplement un sympathisant, mais peut-être que NSSelectorFromString est ce dont vous avez besoin. Il est mentionné explicitement dans le Runtime Reference que vous pouvez l'utiliser pour convertir une chaîne en sélecteur.

Torsten Marek
la source
5
J'ai besoin de rafraîchir mon Google-fu. c'est exactement ce que je cherchais (ou pas comme ça peut être).
craigb
Eh bien, j'avais toujours les liens dans mes signets depuis que j'ai lu la documentation Objective-C 2.0 il y a quelques jours.
Torsten Marek
40

Selon la documentation XCode, votre psuedocode fait les choses correctement.

Il est plus efficace d'attribuer des valeurs aux variables SEL au moment de la compilation avec la directive @selector (). Cependant, dans certains cas, un programme peut avoir besoin de convertir une chaîne de caractères en sélecteur lors de l'exécution. Cela peut être fait avec la fonction NSSelectorFromString:

setWidthHeight = NSSelectorFromString(aBuffer);

Edit: Bummer, trop lent. : P

Josh Gagnon
la source
2
NSStringFromSelector(@"doWork")le convertit dans l'autre sens (juste pour info)
bendytree
8
Je pense que vous voulez dire, NSStringFromSelector (@selector (doWork))
jpswain
Et que fait ce sélecteur? Ne devrions-nous pas spécifier un bloc ou quelque chose?
user4951
12

Je dois dire que c'est un peu plus compliqué que ce que les réponses des répondants précédents pourraient suggérer ... si vous voulez vraiment créer un sélecteur ... pas seulement "appeler un" que vous "avez traîner" .. .

Vous devez créer un pointeur de fonction qui sera appelé par votre "nouvelle" méthode .. donc pour une méthode comme [self theMethod:(id)methodArg];, vous écririez ...

void (^impBlock)(id,id) = ^(id _self, id methodArg) { 
     [_self doSomethingWith:methodArg]; 
};

et ensuite vous devez générer le IMPbloc dynamiquement, cette fois, en passant, "self", le SEL, et tous les arguments ...

void(*impFunct)(id, SEL, id) = (void*) imp_implementationWithBlock(impBlock);

et ajoutez-le à votre classe, avec une signature de méthode précise pour l'ensemble de la ventouse (dans ce cas "v@:@", retour vide, appelant d'objet, argument d'objet)

 class_addMethod(self.class, @selector(theMethod:), (IMP)impFunct, "v@:@");

Vous pouvez voir quelques bons exemples de ce genre de manigances d'exécution , dans l'un de mes dépôts, ici.

Alex Gray
la source
5

Je sais que cela a été répondu depuis longtemps, mais je veux toujours partager. Cela peut être fait en utilisant sel_registerNameaussi.

L'exemple de code dans la question peut être réécrit comme ceci:

SEL selector = sel_registerName("doWork:");
[myobj respondsToSelector:selector];
Krypton
la source
2
En fait, NSSelectorFromStringmentionné par @ torsten-marek utilise sel_registerNamesous le capot. appledev : "NSSelectorFromString transmet une représentation de caractère codé UTF-8 de aSelectorName à sel_registerName et renvoie la valeur renvoyée par cette fonction"
PLG