web-dev-qa-db-fra.com

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

Je sais comment créer un SEL au moment de la compilation en utilisant @selector(MyMethodName:) mais ce que je veux faire est de créer dynamiquement un sélecteur à partir d'un 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 recherché la documentation de l'API Apple Apple, mais je n'ai pas trouvé de moyen qui ne repose pas sur la syntaxe @selector(myTarget:) au moment de la compilation.

92
craigb

Je ne suis pas un programmeur Objective-C, simplement un sympathisant, mais peut-être 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.

180
Torsten Marek

Selon la documentation XCode, votre pseudo-code est fondamentalement correct.

Il est plus efficace d'affecter 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 au moment de l'exécution. Cela peut être fait avec la fonction NSSelectorFromString:

setWidthHeight = NSSelectorFromString(aBuffer);

Edit: Bummer, trop lent. : P

40
Josh Gagnon

Je dois dire que c'est un peu plus compliqué que les réponses des répondants précédents ne le suggèrent ... si vous voulez vraiment créer un sélecteur ... pas seulement "en appeler un" que vous avez "autour de vous" ...

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 dynamiquement le bloc IMP, 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, ainsi qu'une signature de méthode précise pour l'ensemble du meunier (dans ce cas "v@:@", retour nul, appelant objet, argument objet)

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

Vous pouvez voir quelques bons exemples de ce genre de shenanigans d'exécution, dans l'un de mes repos, ici.

11
Alex Gray

Je sais que cela a été répondu depuis longtemps, mais je veux quand même partager. Cela peut être fait en utilisant sel_registerName aussi.

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

SEL selector = sel_registerName("doWork:");
[myobj respondsToSelector:selector];
4
Krypton