Ainda sou meio novo em Objective-C e me pergunto qual é a diferença entre as duas declarações a seguir.
[object performSelector:@selector(doSomething)];
[object doSomething];
Ainda sou meio novo em Objective-C e me pergunto qual é a diferença entre as duas declarações a seguir.
[object performSelector:@selector(doSomething)];
[object doSomething];
Respostas:
Basicamente, performSelector permite que você determine dinamicamente qual seletor chamar um seletor em um determinado objeto. Em outras palavras, o seletor não precisa ser determinado antes do tempo de execução.
Portanto, mesmo que sejam equivalentes:
[anObject aMethod];
[anObject performSelector:@selector(aMethod)];
O segundo formulário permite que você faça isso:
SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];
antes de enviar a mensagem.
performSelector:
é algo que você provavelmente só fará se implementar ação-alvo em sua classe. Os irmãos performSelectorInBackground:withObject:
e performSelectorOnMainThread:withObject:waitUntilDone:
muitas vezes são mais úteis. Para gerar um thread de segundo plano e para chamar de volta os resultados do thread de segundo plano para o thread principal.
performSelector
também é útil para suprimir avisos de compilação. Se você souber que o método existe (como após o uso respondsToSelector
), ele impedirá o Xcode de dizer "pode não responder a your_selector
". Apenas não o use em vez de descobrir a verdadeira causa do aviso. ;)
Para este exemplo básico na questão,
[object doSomething];
[object performSelector:@selector(doSomething)];
não há diferença no que vai acontecer. doSomething será executado de forma síncrona pelo objeto. Apenas "doSomething" é um método muito simples, que não retorna nada e não requer nenhum parâmetro.
fosse algo um pouco mais complicado, como:
(void)doSomethingWithMyAge:(NSUInteger)age;
as coisas ficariam complicadas, porque [object doSomethingWithMyAge: 42];
não pode mais ser chamado com qualquer variante de "performSelector", porque todas as variantes com parâmetros aceitam apenas parâmetros de objeto.
O seletor aqui seria "doSomethingWithMyAge:" mas qualquer tentativa de
[object performSelector:@selector(doSomethingWithMyAge:) withObject:42];
simplesmente não compila. passar um NSNumber: @ (42) em vez de 42 também não ajudaria, porque o método espera um tipo C básico - não um objeto.
Além disso, existem variantes performSelector de até 2 parâmetros, não mais. Embora os métodos muitas vezes tenham muitos mais parâmetros.
Eu descobri que, embora sejam variantes síncronas de performSelector:
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
sempre retornava um objeto, consegui retornar um simples BOOL ou NSUInteger também, e funcionou.
Um dos dois principais usos de performSelector é compor dinamicamente o nome do método que você deseja executar, conforme explicado em uma resposta anterior. Por exemplo
SEL method = NSSelectorFromString([NSString stringWithFormat:@"doSomethingWithMy%@:", @"Age");
[object performSelector:method];
O outro uso é enviar assincronamente uma mensagem para o objeto, que será executado posteriormente no runloop atual. Para isso, existem várias outras variantes do 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;
(sim, reuni-os de várias categorias de classe Foundation, como NSThread, NSRunLoop e NSObject)
Cada uma das variantes tem seu próprio comportamento especial, mas todas compartilham algo em comum (pelo menos quando waitUntilDone está definido como NO). A chamada "performSelector" retornaria imediatamente e a mensagem para o objeto só seria colocada no runloop atual após algum tempo.
Por causa da execução atrasada - naturalmente, nenhum valor de retorno está disponível do método do seletor, portanto, o valor de retorno - (vazio) em todas essas variantes assíncronas.
Espero ter coberto isso de alguma forma ...
@ennuikiller está certo. Basicamente, os seletores gerados dinamicamente são úteis quando você não sabe (e geralmente não pode) saber o nome do método que estará chamando ao compilar o código.
Uma diferença importante é que -performSelector:
e amigos (incluindo as variantes multi-threaded e atrasadas ) são um tanto limitados, pois são projetados para uso com métodos com parâmetros 0-2. Por exemplo, chamar -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:
com 6 parâmetros e retornar o NSString
é muito complicado e não é compatível com os métodos fornecidos.
NSInvocation
objeto.
performSelector:
e todos os amigos aceitam argumentos de objeto, o que significa que você não pode usá-los para chamar (por exemplo) setAlphaValue:
, porque seu argumento é um float.
Os seletores são um pouco como os ponteiros de função em outras linguagens. Você os usa quando não sabe em tempo de compilação qual método deseja chamar em tempo de execução. Além disso, como os ponteiros de função, eles encapsulam apenas a parte do verbo da invocação. Se o método tiver parâmetros, você também precisará passá-los.
Um NSInvocation
serve a um propósito semelhante, exceto pelo fato de reunir mais informações. Não inclui apenas a parte do verbo, também inclui o objeto de destino e os parâmetros. Isso é útil quando você deseja chamar um método em um objeto específico com parâmetros específicos, não agora, mas no futuro. Você pode construir um apropriado NSInvocation
e dispará-lo mais tarde.
Existe outra diferença sutil entre os dois.
[object doSomething]; // is executed right away
[object performSelector:@selector(doSomething)]; // gets executed at the next runloop
Aqui está o trecho da documentação da Apple
"performSelector: withObject: afterDelay: Executa o seletor especificado no encadeamento atual durante o próximo ciclo de loop de execução e após um período de atraso opcional. Como ele espera até o próximo ciclo de loop de execução para realizar o seletor, esses métodos fornecem um mini atraso automático de o código em execução no momento. Vários seletores na fila são executados um após o outro na ordem em que foram colocados na fila. "
performSelector:withObject:afterDelay:
, mas a pergunta e seu trecho estão usando performSelector:
, que é um método totalmente diferente. Dos documentos para ele: <quote> O performSelector:
método é equivalente a enviar uma aSelector
mensagem diretamente para o destinatário. </quote>
performSelector/performSelector:withObject/performSelector:withObject:afterDelay
todos se comportavam da mesma maneira, o que foi um erro.