versiones programación objective lenguaje framework español ejemplos objective-c cocoa

objective c - programación - Cómo usar performSelector: withObject: afterDelay: con tipos primitivos en Cocoa?



versiones de objective c (13)

Encuentro que la manera más rápida (pero algo sucia) de hacer esto es invocando objc_msgSend directamente. Sin embargo, es peligroso invocarlo directamente porque necesita leer la documentación y asegurarse de que está utilizando la variante correcta para el tipo de valor de retorno y porque objc_msgSend se define como vararg para la conveniencia del compilador pero en realidad se implementa como pegamento de ensamblaje rápido. . Aquí hay un código usado para llamar a un método delegado - [delegar integerDidChange:] que toma un solo argumento entero.

#import <objc/message.h> SEL theSelector = @selector(integerDidChange:); if ([self.delegate respondsToSelector:theSelector]) { typedef void (*IntegerDidChangeFuncPtrType)(id, SEL, NSInteger); IntegerDidChangeFuncPtrType MyFunction = (IntegerDidChangeFuncPtrType)objc_msgSend; MyFunction(self.delegate, theSelector, theIntegerThatChanged); }

Esto primero guarda el selector ya que nos referiremos a él varias veces y sería fácil crear un error tipográfico. Luego verifica que el delegado realmente responde al selector; podría ser un protocolo opcional. A continuación, crea un tipo de puntero a función que especifica la firma real del selector. Tenga en cuenta que todos los mensajes de Objective-C tienen dos primeros argumentos ocultos, el objeto al que se envía el mensaje y el selector que se envía. Luego creamos un puntero de función del tipo apropiado y lo configuramos para que apunte a la función objc_msgSend subyacente. Tenga en cuenta que si el valor devuelto es flotante o struct, debe usar una variante diferente de objc_msgSend. Finalmente, envíe el mensaje usando la misma maquinaria que Objective-C usa debajo de las hojas.

El método performSelector:withObject:afterDelay: me permite invocar un método en el objeto con un argumento de objeto después de un cierto tiempo. No se puede utilizar para métodos con un argumento que no sea objeto (por ejemplo, ints, flotantes, estructuras, punteros que no sean objetos, etc.).

¿Cuál es la forma más sencilla de lograr lo mismo con un método con un argumento sin objeto? Sé que para el performSelector:withObject: regular performSelector:withObject: la solución es usar NSInvocation (que por cierto es realmente complicado). Pero no sé cómo manejar la parte de "retraso".

Gracias,


Esto es lo que solía llamar algo que no pude cambiar usando NSInvocation:

SEL theSelector = NSSelectorFromString(@"setOrientation:animated:"); NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature: [MPMoviePlayerController instanceMethodSignatureForSelector:theSelector]]; [anInvocation setSelector:theSelector]; [anInvocation setTarget:theMovie]; UIInterfaceOrientation val = UIInterfaceOrientationPortrait; BOOL anim = NO; [anInvocation setArgument:&val atIndex:2]; [anInvocation setArgument:&anim atIndex:3]; [anInvocation performSelector:@selector(invoke) withObject:nil afterDelay:1];


Llamar a performSelector con un NSNumber u otro NSValue no funcionará. En lugar de usar el valor de NSValue / NSNumber, efectivamente convertirá el puntero en int, float o lo que sea y lo usará.

Pero la solución es simple y obvia. Crear la invocación NS y llamar

[invocation performSelector:@selector(invoke) withObject:nil afterDelay:delay]


Los bloques son el camino a seguir. Puede tener parámetros complejos, tipo de seguridad, y es mucho más simple y más seguro que la mayoría de las respuestas anteriores aquí. Por ejemplo, podrías simplemente escribir:

[MONBlock performBlock:^{[obj setFrame:SOMETHING];} afterDelay:2];

Los bloques le permiten capturar listas de parámetros arbitrarios, objetos de referencia y variables.

Implementación de respaldo (básica):

@interface MONBlock : NSObject + (void)performBlock:(void(^)())pBlock afterDelay:(NSTimeInterval)pDelay; @end @implementation MONBlock + (void)imp_performBlock:(void(^)())pBlock { pBlock(); } + (void)performBlock:(void(^)())pBlock afterDelay:(NSTimeInterval)pDelay { [self performSelector:@selector(imp_performBlock:) withObject:[pBlock copy] afterDelay:pDelay]; } @end

Ejemplo:

int main(int argc, const char * argv[]) { @autoreleasepool { __block bool didPrint = false; int pi = 3; // close enough =p [MONBlock performBlock:^{NSLog(@"Hello, World! pi is %i", pi); didPrint = true;} afterDelay:2]; while (!didPrint) { [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeInterval:0.1 sinceDate:NSDate.date]]; } NSLog(@"(Bye, World!)"); } return 0; }

También vea la respuesta de Michael (+1) para otro ejemplo.


NO USE ESTA RESPUESTA. SÓLO HE SIDO DEJADO PARA PROPÓSITOS HISTÓRICOS. VEA LOS COMENTARIOS A CONTINUACIÓN.

Hay un truco simple si es un parámetro BOOL.

Pase nulo por NO y sí por SÍ. nil se lanza al valor BOOL de NO. el self se lanza al valor BOOL de SÍ.

Este enfoque se rompe si es algo más que un parámetro BOOL.

Asumir auto es una UIView.

//nil will be cast to NO when the selector is performed [self performSelector:@selector(setHidden:) withObject:nil afterDelay:5.0]; //self will be cast to YES when the selector is performed [self performSelector:@selector(setHidden:) withObject:self afterDelay:10.0];


PerformSelector: WithObject siempre toma un objeto, por lo tanto, para pasar argumentos como int / double / float, etc. ..... Puede usar algo como esto.

//NSNumber is an object.. [self performSelector:@selector(setUserAlphaNumber:) withObject: [NSNumber numberWithFloat: 1.0f] afterDelay:1.5]; -(void) setUserAlphaNumber: (NSNumber*) number{ [txtUsername setAlpha: [number floatValue] ]; }

De la misma manera puede usar [NSNumber numberWithInt:] etc .... y en el método de recepción puede convertir el número a su formato como [number int] o [number double].


Puede usar NSTimer para llamar a un selector:

[NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(yourMethod:) userInfo:nil repeats:NO]


Quizás NSValue , solo asegúrese de que sus punteros sigan siendo válidos después de la demora (es decir, que no haya objetos asignados en la pila).


Sé que esta es una vieja pregunta, pero si está creando iOS SDK 4+, entonces puede usar bloques para hacer esto con muy poco esfuerzo y hacerlo más legible:

double delayInSeconds = 2.0; int primitiveValue = 500; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ [self doSomethingWithPrimitive:primitiveValue]; });


Siempre recomendaría que use NSMutableArray como el objeto para transmitir. Esto se debe a que puede pasar varios objetos, como el botón presionado y otros valores. NSNumber, NSInteger y NSString son solo contenedores de algún valor. Asegúrese de que cuando obtiene el objeto de la matriz que se refiere a un tipo de contenedor correcto. Debe pasar contenedores NS. Ahí puedes probar el valor. Recuerde que los contenedores usan isEqual cuando se comparan los valores.

#define DELAY_TIME 5 -(void)changePlayerGameOnes:(UIButton*)sender{ NSNumber *nextPlayer = [NSNumber numberWithInt:[gdata.currentPlayer intValue]+1 ]; NSMutableArray *array = [[NSMutableArray alloc]initWithObjects:sender, nil]; [array addObject:nextPlayer]; [self performSelector:@selector(next:) withObject:array afterDelay:DELAY_TIME]; } -(void)next:(NSMutableArray*)nextPlayer{ if(gdata != nil){ //if game choose next player [self nextPlayer:[nextPlayer objectAtIndex:1] button:[nextPlayer objectAtIndex:0]]; } }


Simplemente envuelva el flotante, booleano, int o similar en un NSNumber.

Para las estructuras, no sé de una solución práctica, pero podrías hacer una clase ObjC separada que posea dicha estructura.


Tal vez ... bueno, muy probablemente me falta algo, pero ¿por qué no simplemente crear un tipo de objeto, digamos NSNumber, como un contenedor para su variable de tipo no objeto, como CGFloat?

CGFloat myFloat = 2.0; NSNumber *myNumber = [NSNumber numberWithFloat:myFloat]; [self performSelector:@selector(MyCalculatorMethod:) withObject:myNumber afterDelay:5.0];


También quería hacer esto, pero con un método que recibe un parámetro BOOL. Envolviendo el valor de bool con NSNumber, NO PASE EL VALOR. No tengo ni idea de porqué.

Así que terminé haciendo un truco simple. Pongo el parámetro requerido en otra función ficticia y llamo a esa función usando performSelector, donde withObject = nil;

[self performSelector:@selector(dummyCaller:) withObject:nil afterDelay:5.0]; -(void)dummyCaller { [self myFunction:YES]; }