目标C为循环延迟

时间:2015-06-17 00:19:35

标签: objective-c for-loop cocos2d-iphone delay grand-central-dispatch

我有一个for循环,我希望在迭代之间添加延迟。我已将waitUntilDone更改为YES并获得相同的结果。我的数组中只有两个数字,两个数字在五秒后调用,而不是:

0s - 没什么 5s - 座叫 10s-座叫

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

如果重要,我正在使用Cocos2d

2 个答案:

答案 0 :(得分:6)

for循环将一个接一个地发送,所以它们基本上会延迟相同的时间 而是为每个设置不同的增加延迟:

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];

                });
}

答案 1 :(得分:1)

@zaph有一个非常好的解决方案。我以为我会从不同的角度尝试。由于Objective-C是 Objective -C,为什么不定义某种对象来进行这种定时循环?提示:这存在。我们可以使用NSTimer及其userInfo属性来解决这个问题。我认为解决方案有点优雅,如果不是讨厌的黑客。

// 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];
    }
}

我会通过添加到运行循环来检查NSTimer是否被保留。如果不是这种情况,你应该将对它的引用存储为管理所有这些的任何类的属性。

值得注意的是,因为NSTimers默认安装在主运行循环上,所以你不必担心所有的GCD内容。然后,如果这项工作非常困难,您可能希望-processNextTransaction:将其工作卸载到另一个GCD队列,然后返回主队列以初始化NSTimer实例。

请务必使用-scheduledTimer...方法; timer...上的NSTimer类方法不会在任何循环上安装它,并且对象只是在空间中无所事事。不要做repeats:YES,这将是悲剧性的,因为你的计时器附带在运行循环上,没有任何引用指向它们知道如何或在何处阻止它们。这通常是一件坏事。

为了避免EXC_BAD_ACCESS例外,永远不要释放NSTimer将调用其方法的对象,如果该计时器尚未触发。您可能希望将待处理的NSTimer存储在类的属性中,以便您可以处理此类事情。如果它是一个管理所有这些(通常是)的ViewController,那么我将使用以下代码来清理-viewWillDisappear上的计时器。 (这假设您要为某些@propertyself.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.
    }
}