iphone - iOS: ¿cómo implementar un performSelector con múltiples argumentos y con afterDelay?
(8)
Soy un novato de iOS. Tengo un método selector de la siguiente manera:
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second
{
}
Estoy tratando de implementar algo como esto -
[self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first" withObject:@"second" afterDelay:15.0];
Pero eso me da un error diciendo -
Instance method -performSelector:withObject:withObject:afterDelay: not found
¿Alguna idea de lo que me estoy perdiendo?
Acabo de hacer algunos swizzling y necesitaba llamar al método original. Lo que hice fue hacer un protocolo y transmitirle mi objetivo. Otra forma es definir el método en una categoría, pero necesitaría la supresión de una advertencia (#pragma clang diagnostic ignored "-Wincomplete-implementation").
La opción más simple es modificar su método para tomar un único parámetro que contenga ambos argumentos, como un NSArray
o NSDictionary
(o agregar un segundo método que tome un solo parámetro, descomprimirlo, y llame al primer método, y luego llamar al segundo método en un retraso).
Por ejemplo, podrías tener algo como:
- (void) fooOneInput:(NSDictionary*) params {
NSString* param1 = [params objectForKey:@"firstParam"];
NSString* param2 = [params objectForKey:@"secondParam"];
[self fooFirstInput:param1 secondInput:param2];
}
Y luego para llamarlo, puedes hacer:
[self performSelector:@selector(fooOneInput:)
withObject:[NSDictionary dictionaryWithObjectsAndKeys: @"first", @"firstParam", @"second", @"secondParam", nil]
afterDelay:15.0];
No me gusta el modo NSInvocation, demasiado complejo. Vamos a mantenerlo simple y limpio:
// Assume we have these variables
id target, SEL aSelector, id parameter1, id parameter2;
// Get the method IMP, method is a function pointer here.
id (*method)(id, SEL, id, id) = (void *)[target methodForSelector:aSelector];
// IMP is just a C function, so we can call it directly.
id returnValue = method(target, aSelector, parameter1, parameter2);
Personalmente, creo que una solución más cercana a sus necesidades es el uso de NSInvocation.
Algo como lo siguiente hará el trabajo:
indexPath y dataSource son dos variables de instancia definidas en el mismo método.
SEL aSelector = NSSelectorFromString(@"dropDownSelectedRow:withDataSource:");
if([dropDownDelegate respondsToSelector:aSelector]) {
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[dropDownDelegate methodSignatureForSelector:aSelector]];
[inv setSelector:aSelector];
[inv setTarget:dropDownDelegate];
[inv setArgument:&(indexPath) atIndex:2]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation
[inv setArgument:&(dataSource) atIndex:3]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation
[inv invoke];
}
Porque no existe un [NSObject performSelector:withObject:withObject:afterDelay:]
.
Necesita encapsular los datos que desea enviar en un solo objeto Objective C (por ejemplo, un NSArray, un NSDictionary, algún tipo personalizado de Objective C) y luego pasarlo a través del [NSObject performSelector:withObject:afterDelay:]
que está bien conocido y amado
Por ejemplo:
NSArray * arrayOfThingsIWantToPassAlong =
[NSArray arrayWithObjects: @"first", @"second", nil];
[self performSelector:@selector(fooFirstInput:)
withObject:arrayOfThingsIWantToPassAlong
afterDelay:15.0];
Puede empaquetar sus parámetros en un objeto y usar un método de ayuda para llamar a su método original como Michael, y otros ahora, han sugerido.
Otra opción es dispatch_after, que tomará un bloque y lo enrutará en un momento determinado.
double delayInSeconds = 15.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self fooFirstInput:first secondInput:second];
});
O, como ya ha descubierto, si no necesita el retraso, puede usar - performSelector:withObject:withObject:
Puede encontrar todos los tipos de métodos ofrecidos por Selector: aquí:
Hay un montón de variaciones, pero no hay una versión que tome varios objetos, así como un retraso. En su lugar, deberá finalizar sus argumentos en un NSArray o NSDictionary.
- performSelector:
- performSelector:withObject:
- performSelector:withObject:withObject:
– performSelector:withObject:afterDelay:
– performSelector:withObject:afterDelay:inModes:
– performSelectorOnMainThread:withObject:waitUntilDone:
– performSelectorOnMainThread:withObject:waitUntilDone:modes:
– performSelector:onThread:withObject:waitUntilDone:
– performSelector:onThread:withObject:waitUntilDone:modes:
– performSelectorInBackground:withObject:
- (void) callFooWithArray: (NSArray *) inputArray
{
[self fooFirstInput: [inputArray objectAtIndex:0] secondInput: [inputArray objectAtIndex:1]];
}
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second
{
}
y llámalo con:
[self performSelector:@selector(callFooWithArray) withObject:[NSArray arrayWithObjects:@"first", @"second", nil] afterDelay:15.0];