Como posso criar dinamicamente um seletor em tempo de execução com Objective-C?

93

Eu sei como criar um SELem tempo de compilação usando, @selector(MyMethodName:)mas o que eu quero fazer é criar um seletor dinamicamente de um NSString. Isso é mesmo possível?

O que eu posso fazer:

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

O que eu quero fazer: (pseudocódigo, obviamente não funciona)

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

Tenho pesquisado os documentos da API da Apple, mas não encontrei uma maneira que não dependa da @selector(myTarget:)sintaxe de tempo de compilação .

craigb
fonte

Respostas:

180

Não sou um programador Objective-C, apenas simpatizante, mas talvez NSSelectorFromString seja o que você precisa. É mencionado explicitamente na Referência de tempo de execução que você pode usá-lo para converter uma string em um seletor.

Torsten Marek
fonte
5
Eu preciso atualizar meu Google-fu. isso é exatamente o que eu estava (ou não era) procurando.
craigb de
Bem, eu ainda tinha os links voando em meus favoritos desde que li a documentação do Objective-C 2.0 alguns dias atrás.
Torsten Marek,
40

De acordo com a documentação do XCode, seu psuedocode basicamente acerta.

É mais eficiente atribuir valores às variáveis ​​SEL em tempo de compilação com a diretiva @selector (). No entanto, em alguns casos, um programa pode precisar converter uma string de caracteres em um seletor em tempo de execução. Isso pode ser feito com a função NSSelectorFromString:

setWidthHeight = NSSelectorFromString(aBuffer);

Edit: Bummer, muito lento. : P

Josh Gagnon
fonte
2
NSStringFromSelector(@"doWork")converte-o de outra maneira (apenas fyi)
bendytree
8
Acho que você quer dizer NSStringFromSelector (@selector (doWork))
jpswain
E o que esse seletor supostamente faz? Não deveríamos especificar um bloco ou algo assim?
user4951
12

Devo dizer que é um pouco mais complicado do que as respostas dos entrevistados anteriores podem sugerir ... se você realmente deseja criar um seletor ... não apenas "chamar aquele" que você "tem por aí" .. .

Você precisa criar um ponteiro de função que será chamado pelo seu "novo" método .. então, para um método como [self theMethod:(id)methodArg];, você escreveria ...

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

e então você precisa gerar o IMPbloco dinamicamente, desta vez, passando, "self", o SEL, e quaisquer argumentos ...

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

e adicione-o à sua classe, junto com uma assinatura de método precisa para todo o sugador (neste caso "v@:@", retorno void, chamador de objeto, argumento de objeto)

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

Você pode ver alguns bons exemplos desse tipo de travessura em tempo de execução , em um dos meus repositórios, aqui.

Alex Gray
fonte
5

Eu sei que isso foi respondido há muito tempo, mas ainda quero compartilhar. Isso também pode ser feito usando sel_registerName.

O código de exemplo na pergunta pode ser reescrito assim:

SEL selector = sel_registerName("doWork:");
[myobj respondsToSelector:selector];
Krypton
fonte
2
Na verdade, NSSelectorFromStringmencionado por @ torsten-marek usa sel_registerNamesob o capô. appledev : "NSSelectorFromString passa uma representação de caracteres codificados em UTF-8 de aSelectorName para sel_registerName e retorna o valor retornado por essa função"
PLG