¿Cómo puedo crear dinámicamente un selector en tiempo de ejecución con Objective-C?

93

Sé cómo crear un SELen tiempo de compilación usando, @selector(MyMethodName:)pero lo que quiero hacer es crear un selector dinámicamente desde un archivo NSString. ¿Es esto siquiera posible?

Que puedo hacer:

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

Lo que quiero hacer: (pseudocódigo, esto obviamente no funciona)

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

He estado buscando los documentos de la API de Apple, pero no he encontrado una forma que no dependa de la @selector(myTarget:)sintaxis en tiempo de compilación .

craigb
fuente

Respuestas:

180

No soy un programador de Objective-C, simplemente un simpatizante, pero tal vez NSSelectorFromString es lo que necesitas. Se menciona explícitamente en Runtime Reference que puede usarlo para convertir una cadena en un selector.

Torsten Marek
fuente
5
Necesito repasar mi Google-fu. eso es exactamente lo que estaba (o no era lo que podría estar) buscando.
craigb
Bueno, todavía tenía los enlaces volando en mis marcadores desde que leí los documentos de Objective-C 2.0 hace un par de días.
Torsten Marek
40

De acuerdo con la documentación de XCode, su psuedocode básicamente lo hace bien.

Es más eficiente asignar valores a las variables SEL en tiempo de compilación con la directiva @selector (). Sin embargo, en algunos casos, es posible que un programa deba convertir una cadena de caracteres en un selector en tiempo de ejecución. Esto se puede hacer con la función NSSelectorFromString:

setWidthHeight = NSSelectorFromString(aBuffer);

Editar: Lástima, demasiado lento. :PAGS

Josh Gagnon
fuente
2
NSStringFromSelector(@"doWork")lo convierte al revés (solo para su información)
bendytree
8
Creo que te refieres a NSStringFromSelector (@selector (doWork))
jpswain
¿Y qué supuestamente hace ese selector? ¿No deberíamos especificar un bloque o algo así?
user4951
12

Debo decir que es un poco más complicado de lo que podrían sugerir las respuestas de los encuestados anteriores ... si realmente desea crear un selector ... no solo "llamar a uno" que "tienes por ahí" ... .

Necesita crear un puntero de función que será llamado por su método "nuevo" ... así que para un método como [self theMethod:(id)methodArg];, escribiría ...

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

y luego necesitas generar el IMPbloque dinámicamente, esta vez, pasando, "self", el SEL, y cualquier argumento ...

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

y agréguelo a su clase, junto con una firma de método precisa para todo el lechón (en este caso "v@:@", retorno vacío, llamador de objeto, argumento de objeto)

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

Puedes ver algunos buenos ejemplos de este tipo de travesuras en tiempo de ejecución , en uno de mis repositorios, aquí.

Alex Grey
fuente
5

Sé que esto ha sido respondido hace mucho tiempo, pero aún quiero compartirlo. Esto también se puede hacer usando sel_registerName.

El código de ejemplo en la pregunta se puede reescribir así:

SEL selector = sel_registerName("doWork:");
[myobj respondsToSelector:selector];
Criptón
fuente
2
En realidad, NSSelectorFromStringmencionado por @ torsten-marek usa sel_registerNamebajo el capó. appledev : "NSSelectorFromString pasa una representación de caracteres codificados en UTF-8 de aSelectorName a sel_registerName y devuelve el valor devuelto por esa función"
PLG