2009-09-29 9 views

Respuesta

183

Básicamente performSelector le permite determinar dinámicamente qué selector llamar a un selector en el objeto dado. En otras palabras, el selector no necesita determinarse antes del tiempo de ejecución.

Por lo tanto a pesar de que éstos son equivalentes:

[anObject aMethod]; 
[anObject performSelector:@selector(aMethod)]; 

La segunda forma le permite hacer esto:

SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation(); 
[anObject performSelector: aSelector]; 

antes de enviar el mensaje.

+3

Vale la pena señalar que en realidad le asigne el resultado de findTheAppropriateSelectorForTheCurrentSituation() para aSelector y luego invoque [unObjeto performSelector: aSelector]. @selector produce un SEL. –

+4

El uso de 'performSelector:' es algo que probablemente solo haga si implementa action-target en su clase. Los hermanos 'performSelectorInBackground: withObject:' y 'performSelectorOnMainThread: withObject: waitUntilDone:' a menudo son más útiles. Para generar un hilo de fondo y para devolver los resultados al hilo principal de dicho hilo de fondo. – PeyloW

+1

'performSelector' también es útil para suprimir las advertencias de compilación. Si sabe que el método existe (como después de usar 'respondsToSelector'), detendrá que Xcode diga" puede que no responda a 'your_selector'". Simplemente no lo use _en lugar_ de averiguar la causa real de la advertencia.;) – Marc

11

@ennuikiller es perfecto. Básicamente, los selectores generados dinámicamente son útiles cuando no se conoce (y generalmente no se puede) el nombre del método al que se llama cuando se compila el código.

Una diferencia clave es que -performSelector: y sus amigos (incluido el multi-threaded and delayed variants) son algo limitados en cuanto a que están diseñados para su uso con métodos con 0-2 parámetros. Por ejemplo, llamar al -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation: con 6 parámetros y devolver el NSString es bastante difícil de manejar, y no es compatible con los métodos proporcionados.

+4

Para hacer eso, necesitarías usar un objeto 'NSInvocation'. –

+6

Otra diferencia: 'performSelector:' y todos los amigos toman argumentos de objeto, lo que significa que no puede usarlos para llamar (por ejemplo) 'setAlphaValue:', porque su argumento es un flotante. – Chuck

+0

Ambos son excelentes puntos. –

3

Los selectores son un poco como punteros a funciones en otros idiomas. Los usa cuando no sabe en tiempo de compilación a qué método desea llamar en tiempo de ejecución. Además, al igual que los punteros de función, solo encapsulan la parte de invocación del verbo. Si el método tiene parámetros, deberá pasarlos también.

Un NSInvocation tiene un propósito similar, excepto que une más información. No solo incluye la parte del verbo, también incluye el objeto objetivo y los parámetros. Esto es útil cuando desea llamar a un método en un objeto particular con parámetros particulares, no ahora sino en el futuro. Puede construir un NSInvocation apropiado y desencadenarlo más tarde.

+5

Los selectores en realidad no se parecen en nada a un puntero de función, ya que un puntero de función es algo que puede invocarse con argumentos y un selector puede utilizarse para llamar a un método particular en cualquier objeto que lo implemente; un selector no tiene el contexto completo de invocación como un puntero de función. – bbum

+1

Los selectores no son lo mismo que los indicadores de función, pero sigo creyendo que son similares. Ellos representan los verbos. Los punteros de función C también representan verbos. Ninguno de los dos es útil sin contexto adicional. Los selectores requieren un objeto y parámetros; los punteros de función requieren parámetros (que pueden incluir un objeto sobre el cual operar). Mi punto era destacar cómo son diferentes de los objetos de Invocación NS, que sí contienen todo el contexto necesario. Tal vez mi comparación fue confusa, en cuyo caso me disculpo. –

+1

Los selectores no son indicadores de función. Ni siquiera cerca. Son cadenas de C simples en la realidad, que contienen un "nombre" de un método (en oposición a "función"). Ni siquiera son firmas de métodos, porque no incrustan los tipos de parámetros. Un objeto puede tener más de un método para el mismo selector (diferentes tipos de param o diferentes tipos de devolución). –

-6

Hay otra diferencia sutil entre los dos.

[object doSomething]; // is executed right away 

    [object performSelector:@selector(doSomething)]; // gets executed at the next runloop 

Aquí es el extracto de la documentación de Apple

"performSelector: withObject: afterDelay: Realiza el selector especificado en el subproceso actual durante el próximo ciclo de bucle de ejecución y después de un período de retardo opcional, ya que. Espera hasta el próximo ciclo de ciclo de ejecución para realizar el selector, estos métodos proporcionan un mini retardo automático desde el código que se está ejecutando actualmente. Varios selectores en cola se ejecutan uno tras otro en el orden en que se pusieron en cola. "

+1

Su respuesta es objetivamente incorrecta. La documentación que cita es sobre 'performSelector: withObject: afterDelay:', pero la pregunta y su fragmento están usando ['performSelector:'] (http://developer.apple.com/library/mac/documentation/Cocoa/Reference/ Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html # // apple_ref/doc/uid/20000052-BBCBGHFG), que es un método completamente diferente. De los documentos para él: El método 'performSelector:' es equivalente a enviar un mensaje 'aSelector' directamente al receptor.

+2

gracias Josh por la aclaración. Estás en lo correcto; Pensé que 'performSelector/performSelector: withObject/performSelector: withObject: afterDelay' se comportó de la misma manera, lo que fue un error. – avi

+0

Hola avi. ¿Alguna posibilidad de que puedas eliminar esta respuesta? Parece que estás de acuerdo en que es una sorpresa;) – robinCTS

10

Para este ejemplo muy básico en la pregunta,

[object doSomething]; 
[object performSelector:@selector(doSomething)]; 

no hay ninguna diferencia en lo que va a ocurrir. doSomething se ejecutará sincrónicamente por objeto. Solo "doSomething" es un método muy simple, que no devuelve nada, y no requiere ningún parámetro.

eran algo un poco más complicado, como:

(void)doSomethingWithMyAge:(NSUInteger)age; 

las cosas se complican, porque [objeto doSomethingWithMyAge: 42];

ya no se puede llamar con ninguna variante de "performSelector", porque todas las variantes con parámetros solo aceptan parámetros de objeto.

El selector aquí habría "doSomethingWithMyAge:" pero cualquier intento de

[object performSelector:@selector(doSomethingWithMyAge:) withObject:42]; 

simplemente no se compilará. pasar un NSNumber: @ (42) en lugar de 42, tampoco ayudaría, porque el método espera un tipo de C básico, no un objeto.

Además, hay variantes de performSelector de hasta 2 parámetros, no más. Mientras que los métodos muchas veces tienen muchos más parámetros.

He descubierto que aunque las variantes sincrónicas de performSelector:

- (id)performSelector:(SEL)aSelector; 
- (id)performSelector:(SEL)aSelector withObject:(id)object; 
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2; 

siempre devuelven un objeto, yo era capaz de devolver un BOOL simple o NSUInteger también, y ha funcionado.

Uno de los dos usos principales de performSelector es componer dinámicamente el nombre del método que desea ejecutar, como se explicó en una respuesta anterior. Por ejemplo

SEL method = NSSelectorFromString([NSString stringWithFormat:@"doSomethingWithMy%@:", @"Age"); 
[object performSelector:method]; 

El otro uso, es enviar un mensaje de forma asíncrona a oponerse, que será ejecutado más tarde en el runloop actual. Para esto, hay varias otras variantes de 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; 

(sí juntélos de varias categorías de clase de la Fundación, como NSThread, NSRunLoop y NSObject)

Cada una de las variantes tiene su propio comportamiento especial, pero todos comparten algo en común (al menos cuando waitUntilDone está configurado a NO). La llamada "performSelector" volverá inmediatamente, y el mensaje a objeto solo se pondrá en el runloop actual después de un tiempo.

Debido a la ejecución retrasada, naturalmente no hay valor de retorno disponible del método del selector, de ahí el valor de retorno (vacío) en todas estas variantes asincrónicas.

espero que cubrí esto de alguna manera ...

Cuestiones relacionadas