2010-10-24 12 views
85

A menudo quiero ejecutar algunos códigos en unos microsegundos en el futuro. Ahora mismo, resolver de esta manera:Bloques en lugar de performSelector: withObject: afterDelay:

- (void)someMethod 
{ 
    // some code 
} 

Y esto:

[self performSelector:@selector(someMethod) withObject:nil afterDelay:0.1]; 

Funciona, pero tengo que crear un nuevo método cada vez. ¿Es posible usar bloques en lugar de esto? Básicamente estoy buscando un método como:

[self performBlock:^{ 
    // some code 
} afterDelay:0.1]; 

Eso me sería realmente útil.

+2

Esto produjo un mes después: http://stackoverflow.com/questions/4139219/how-do-you -trigger-a-block-after-a-delay-like-performselectorwithobjectafter – Jacksonkr

Respuesta

104

No hay manera integrada para hacer eso, pero no es demasiado malo para agregar a través de una categoría:

@implementation NSObject (PerformBlockAfterDelay) 

- (void)performBlock:(void (^)(void))block 
      afterDelay:(NSTimeInterval)delay 
{ 
    block = [[block copy] autorelease]; 
    [self performSelector:@selector(fireBlockAfterDelay:) 
       withObject:block 
       afterDelay:delay]; 
} 

- (void)fireBlockAfterDelay:(void (^)(void))block { 
    block(); 
} 

@end 

crédito a Mike Ash para la implementación básica.

+5

nota: siempre debe elegir un prefijo para sus métodos de categoría, como 'mon_performBlock: afterDelay:'. esto reducirá la posibilidad de que sus métodos colisionen con otras implementaciones. El ejemplo más común: Apple decide agregar este método: ¡Uy !, tu método no reemplazará lo que ya está cargado y si lo hiciera ... sería aún más doloroso. – justin

+8

Debo notar que la idea de '[self performBlock:^{/ * some block * /} afterDelay: 0.1]' no tiene mucho sentido. ¿Por qué esto está unido a un objeto en absoluto? ¿Qué papel tiene 'self 'en el disparo del bloque? Sería mejor que escribieras una función C 'RunBlockAfterDelay (void (^ block) (void), NSTimeInterval delay)', aunque esto requiere crear un objeto temporal cuyo único trabajo es implementar '-fireBlockAfterDelay:'. –

+19

@Kevin: solo por conveniencia. Si lo desea correctamente, usar GDC directamente y llamar a la función 'dispatch_after (dispatch_time_t, dispatch_queue_t, dispatch_block_t)' es la forma correcta de hacerlo. – PeyloW

39

Aquí es una técnica sencilla, basada en GCD, que estoy usando:

void RunBlockAfterDelay(NSTimeInterval delay, void (^block)(void)) 
{ 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC*delay), 
     dispatch_get_current_queue(), block); 
} 

No soy un experto GCD, y estaría interesado en los comentarios de esta solución.

+1

Esto hará lo que quiera, pero le animo para continuar especificando la cola de destino explícitamente; no es mucho más código, y deja muy claro en qué contexto de ejecución se ejecutarán las cosas. Una macro para dispatch_time (DISPATCH_TIME_NOW, NSEC_PER_SEC * delay) podría ser una forma más efectiva de reducir el texto repetitivo. –

+3

Lo único que agregaría es que tener los parámetros al revés haría las invocaciones un poco más intuitivas, ya que su orden seguiría el orden de los sustantivos en el nombre de la función. – danyowdee

+3

@danyowdee Como se mencionó en otra respuesta, eso está mal. "Un bloque siempre debe ser el último argumento para un método" según http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html – SilverSideDown

15

Si específicamente necesita un retraso mayor, las soluciones anteriores funcionan bien. He utilizado el enfoque de @ nick con gran éxito.

Sin embargo, si lo que desea su bloque a ejecutar durante la siguiente iteración del bucle principal, puede recortar hacia abajo aún más con sólo el siguiente:

[[NSOperationQueue mainQueue] addOperationWithBlock:aBlock]; 

Esto es similar a usar performSelector: con afterDelay de 0.0f

+4

+1, pero una ventaja para el performSelector existente es la capacidad de cancelar. Ese es un problema menor con retraso cero, pero incluso así puede ser útil para resolver carreras potenciales. – JLundell

21

Otra forma (tal vez la peor manera de hacer esto por muchas razones) es:

[UIView animateWithDuration:0.0 delay:5.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{ 
} completion:^(BOOL finished) { 
    //do stuff here 
}]; 
11

que utiliza un código similar como esto:

double delayInSeconds = 0.2f; 
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 
     //whatever you wanted to do here... 
    }); 
+0

esta es la solución correcta, puede parecer complicado pero Xcode completa automáticamente el patrón al escribir en dispatch_after. – malhal

Cuestiones relacionadas