Todavía soy algo nuevo en Objective-C y me pregunto cuál es la diferencia entre las siguientes dos declaraciones.
[object performSelector:@selector(doSomething)];
[object doSomething];
Todavía soy algo nuevo en Objective-C y me pregunto cuál es la diferencia entre las siguientes dos declaraciones.
[object performSelector:@selector(doSomething)];
[object doSomething];
Respuestas:
Básicamente, performSelector le permite determinar dinámicamente a qué selector llamar a un selector en el objeto dado. En otras palabras, no es necesario determinar el selector antes del tiempo de ejecución.
Por lo tanto, aunque estos son equivalentes:
[anObject aMethod];
[anObject performSelector:@selector(aMethod)];
El segundo formulario le permite hacer esto:
SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];
antes de enviar el mensaje.
performSelector:
es algo que probablemente solo haga si implementa target-action en su clase. Los hermanos performSelectorInBackground:withObject:
ya performSelectorOnMainThread:withObject:waitUntilDone:
menudo son más útiles. Para generar un hilo en segundo plano y para devolver los resultados al hilo principal desde dicho hilo en segundo plano.
performSelector
también es útil para suprimir las advertencias de compilación. Si sabe que el método existe (como después de usarlo respondsToSelector
), evitará que Xcode diga "puede que no responda your_selector
". Simplemente no lo use en lugar de averiguar la causa real de la advertencia. ;)
Para este ejemplo muy básico en la pregunta,
[object doSomething];
[object performSelector:@selector(doSomething)];
no hay diferencia en lo que va a pasar. 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.
si fuera algo un poco más complicado, como:
(void)doSomethingWithMyAge:(NSUInteger)age;
las cosas se complicarían, porque [object 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í serí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 C básico, no un objeto.
Además, existen variantes de performSelector de hasta 2 parámetros, no más. Mientras que los métodos muchas veces tienen muchos más parámetros.
Descubrí 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 devuelve un objeto, también pude devolver un BOOL simple o NSUInteger, y funcionó.
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 de forma asincrónica un mensaje al objeto, que se ejecutará más tarde en el runloop actual. Para ello, existen 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í, los reuní de varias categorías de clases de la Fundación, como NSThread, NSRunLoop y NSObject)
Cada una de las variantes tiene su propio comportamiento especial, pero todas comparten algo en común (al menos cuando waitUntilDone se establece en NO). La llamada "performSelector" volverá inmediatamente y el mensaje al objeto solo se pondrá en el runloop actual después de un tiempo.
Debido a la ejecución retrasada, naturalmente, no hay ningún valor de retorno disponible desde el método del selector, de ahí el valor de retorno - (vacío) en todas estas variantes asincrónicas.
Espero haber cubierto esto de alguna manera ...
@ennuikiller da en el clavo. Básicamente, los selectores generados dinámicamente son útiles para cuando no (y normalmente no es posible) sabe el nombre del método al que llamará cuando compile el código.
Una diferencia clave es que -performSelector:
y los amigos (incluidas las variantes de subprocesos múltiples y retardados ) son algo limitados en el sentido de que están diseñados para su uso con métodos con parámetros 0-2. Por ejemplo, llamar -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.
NSInvocation
objeto.
performSelector:
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.
Los selectores son un poco como punteros de función 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 verbal de la invocación. Si el método tiene parámetros, deberá pasarlos también.
An 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 de destino 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 apropiado NSInvocation
y dispararlo más tarde.
Hay otra sutil diferencia entre los dos.
[object doSomething]; // is executed right away
[object performSelector:@selector(doSomething)]; // gets executed at the next runloop
Aquí está el extracto de la documentación de Apple
"performSelector: withObject: afterDelay: realiza el selector especificado en el subproceso actual durante el siguiente ciclo de bucle de ejecución y después de un período de retraso opcional. Debido a que espera hasta el siguiente ciclo de bucle de ejecución para realizar el selector, estos métodos proporcionan un mini retraso automático desde el código que se está ejecutando actualmente. Se realizan varios selectores en cola uno tras otro en el orden en que se pusieron en cola ".
performSelector:withObject:afterDelay:
, pero la pregunta y su fragmento están usando performSelector:
, que es un método completamente diferente. De los documentos para ello: <quote> El performSelector:
método es equivalente a enviar un aSelector
mensaje directamente al receptor. </quote>
performSelector/performSelector:withObject/performSelector:withObject:afterDelay
todos se comportaban de la misma manera, lo cual fue un error.