objective c - Objetivo C para retardo de bucle
objective-c for-loop (2)
Tengo un ciclo for que quiero agregar un retraso entre iteraciones. He cambiado waitUntilDone a YES y obtengo los mismos resultados. Mi matriz solo tiene dos números y se llaman a los dos después de los cinco segundos en lugar de:
0s - nada 5s - Bloque llamado 10s - Bloque llamado
for(NSNumber* transaction in gainsArray) {
double delayInSeconds = 5.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSLog(@"Block");
[self performSelectorOnMainThread:@selector(private_addTransactionToBankroll:)
withObject:transaction waitUntilDone:NO];
});
}
2015-06-16 20:11:06.485 TestApp[97027:6251126] Block
2015-06-16 20:11:06.485 TestApp[97027:6251127] Block
Estoy usando Cocos2d si eso importa
@zaph tiene una solución bastante buena. Pensé que lo intentaría desde un ángulo diferente. Como Objective-C es Objective -C, ¿por qué no definir algún tipo de objeto para hacer este bucle cronometrado? Sugerencia: esto existe. Podemos usar NSTimer y su propiedad userInfo para resolver esto. Creo que la solución es algo elegante, si no un hack desagradable.
// Somewhere in code.... to start the ''loop''
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
action:@selector(processNextTransaction:)
userInfo:@{
@"gains": [gainsArray mutableCopy]
}
repeats:NO];
// What handles each ''iteration'' of your ''loop''
- (void)processNextTransaction:(NSTimer *)loopTimer {
NSMutableArray *gains = [loopTimer.userInfo objectForKey:@"gains"];
if(gains && gains.count > 0) {
id transaction = [gains firstObject];
[gains removeObjectAtIndex:0]; // NSMutableArray should really return the object we''re removing, but it doesn''t...
[self private_addTransactionToBankroll:transaction];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
action:@selector(processNextTransaction:)
userInfo:@{
@"gains": gains
}
repeats:NO];
}
}
Verificaría que el NSTimer se conserve al agregarlo al ciclo de ejecución. Si ese no es el caso, debe almacenar una referencia como propiedad en cualquier clase que administre todo esto.
También vale la pena señalar que debido a que los NSTimers se instalan en el ciclo de ejecución principal de forma predeterminada, no es necesario preocuparse por todo el material de GCD. Por otra parte, si este trabajo es bastante difícil, es posible que desee -processNextTransaction:
descargar su trabajo en otra cola GCD y luego volver a la cola principal para inicializar la instancia NSTimer.
Asegúrese de utilizar el método -scheduledTimer...
; timer...
los métodos de clase en NSTimer
no lo instalan en ningún ciclo, y los objetos simplemente se quedan en el espacio sin hacer nada. No hagas repeats:YES
, eso sería trágico, ya que tendrías temporizadores conectados al ciclo de ejecución de cualquier manera, sin referencias que los señalaran para saber cómo o dónde detenerlos. Esto es generalmente algo malo.
Para evitar excepciones de EXC_BAD_ACCESS
, nunca desloque el objeto cuyo método va a llamar un NSTimer
, si ese temporizador no se ha NSTimer
aún. Es posible que desee almacenar el NSTimer
pendiente en una propiedad de su clase para que pueda manejar este tipo de cosas. Si se trata de un ViewController que está gestionando todo esto (que normalmente es), entonces usaría el siguiente código para limpiar el temporizador en -viewWillDisappear
. (Esto supone que está configurando un nuevo temporizador en algún @property
, self.timer
)
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if(self.timer) {
[self.timer invalidate]; // -invalidate removes it from the run loop.
self.timer = nil; // Stop pointing at it so ARC destroys it.
}
}
El bucle for enviará uno justo después del otro, de modo que esencialmente se retrasarán por el mismo tiempo.
En cambio, establezca un retraso creciente diferente para cada uno:
double delayInSeconds = 0.0;
for(NSNumber* transaction in gainsArray)
{
delayInSeconds += 5.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
{
NSLog(@"Block");
[self performSelectorOnMainThread:@selector(private_addTransactionToBankroll:)
withObject:transaction
waitUntilDone:NO];
});
}