cómo llamar a un método de múltiples argumentos con retraso

I'm trying to call a method after some delay.

I know there is a solution for that:

[self performSelector:@selector(myMethod) withObject:nil afterDelay:delay];

Vi esta pregunta y Documentación

But my question is: How can I call a method that takes two parameters??

por ejemplo:

- (void) MoveSomethigFrom:(id)from To:(id)to;

How would I call this method with delay, using performSelector:withObject:afterDelay:

Muchas Gracias

preguntado el 09 de marzo de 12 a las 13:03

6 Respuestas

use dispatch_after:

double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    //code to be executed on the main queue after delay
    [self MoveSomethingFrom:from To:to];
});

EDIT 2015: For Swift, i recommend using this small helper method: dispatch_after - GCD in swift?

contestado el 23 de mayo de 17 a las 13:05

Either that, or you can put as many parameters as you want in a container and send it as an argument for the performSelector: withObject: afterDelay método. - Alexander

This seems a good approach, it really delays the call. But it happens something strange, the method doesn't do what is expected, does something else, that I can't even understand.. - Fraile

got it.. It was some code that was executed during the delay.. thanks - Fraile

is there a way to cancel scheduled call? - virus

if someone is interested this should work to cancel scheduled call (if performSelector:withObject:afterDelay: was used): [NSObject cancelPreviousPerformRequestsWithTarget:yourTarget selector:aSelector object: anArgument]; - virus

You can also implement method in NSObject's category using NSInvocation object (works in all versions of iOS). I guess it should be something like this:

@interface NSObject(DelayedPerform)

- (void)performSelector:(SEL)aSelector withObject:(id)argument0 withObject:(id)argument1  afterDelay:(NSTimeInterval)delay {

  NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];

  NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
  [invocation setTarget:self];
  [invocation setSelector:aSelector];
  [invocation setArgument:&argument0 atIndex:2];
  [invocation setArgument:&argument1 atIndex:3];

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

}

@end

respondido 09 mar '12, 18:03

you can use it in >= 3.2 as well. I just wanted to show that it's not necessary to use GCD. I'll edit my original answer) - Eldar Markov

Ok.. I'm sorry but, what is GCD? what's the problem using it? - Fraile

I'm asking, because I have three answers and the 3 are good. Just trying to understand which is the best one to use. Thanks - Fraile

GCD - it's Grand Central Dispatch. Part of iOS sdk 4.0 and higher.If you want to use it in all versions of iOS use mine version) - Eldar Markov

Yes if you really want to deploy on devices < 4.0 you have to stay away from GCD. But developing for iOS 4+ is safe as over 90% of active devices are running it and testing/coding effort is heavily reduced (assuming that you do serious testing) - Martín Ullrich

Otras ideas:

1) You could use NSInvocations:

+ (NSInvocation *)invocationWithMethodSignature:(NSMethodSignature *)signature
(>> see Eldar Markov's answer)

Documentación:
https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSInvocation_Class/Reference/Reference.html

2) You could use a helper method..

[self performSelector:@selector(helperMethod) withObject:nil afterDelay:delay];

- (void) helperMethod
{
    // of course x1 and x2 have to be safed somewhere else
    [object moveSomethigFrom: x1 to: x2];
}

3) You could use an array or a dictionary as parameter..

NSArray* array = [NSArray arrayWithObjects: x1, x2, nil];
[self performSelector:@selector(handleArray:) withObject:array afterDelay:delay];

- (void) handleArray: (NSArray*) array
{
    [object moveSomethigFrom: [array objectAtIndex: 0] to: [array objectAtIndex: 1]];
}

contestado el 23 de mayo de 17 a las 12:05

As a suggestion, don't reference other answers on SO as being "above" or "below", as they can be sorted more ways than one, and that most people sort by rating, which can be volatile. I suggest hitting the "share" button beneath the answer and sharing a link to it, for incase a question ends up with a large number of answers. - ArtOfWarfare

Swift:

    let delayInSeconds = 3.0;
    let delay = delayInSeconds * Double(NSEC_PER_SEC)
    let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay));
    dispatch_after(popTime, dispatch_get_main_queue(), {
        // DO SOMETHING AFTER 3 sec
    });

Respondido 25 Oct 14, 12:10

Here is how you can trigger a block after a delay in Swift:

runThisAfterDelay(seconds: 5) { () -> () in
    print("Prints this 5 seconds later in main queue")
    //Or perform your selector here
}

/// EZSwiftExtensions
func runThisAfterDelay(seconds seconds: Double, after: () -> ()) {
    let time = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
    dispatch_after(time, dispatch_get_main_queue(), after)
}

The argument count does not really matter.

Está incluido como una función estándar en mi repositorio: https://github.com/goktugyil/EZSwiftExtensions

Respondido el 01 de diciembre de 15 a las 22:12

These will all work, but are all much more complex than is needed.

Design the method to be called with an NSDictionary argument. Put the objects in it you need.

If you want the method to be accessible by other means as well, call instead a method that 'unwraps' the dictionary and calls the intended method with explicit parameters.

contestado el 23 de mayo de 14 a las 05:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.