Objective C for循环延迟

Objective C for loop delay

我有一个 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

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

                });
}

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

// 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 添加到 运行 循环来检查是否保留了它。如果不是这种情况,您应该将对它的引用存储为 属性 在 class 管理所有这些的任何东西上。

还值得注意的是,因为默认情况下 NSTimers 安装在主 运行 循环中,所以您无需担心所有 GCD 内容。话又说回来,如果这项工作是相当困难的工作,您可能希望 -processNextTransaction: 将其工作卸载到另一个 GCD 队列,然后返回到主队列来初始化 NSTimer 实例。

一定要使用-scheduledTimer...方法timer... class NSTimer 上的方法不会将其安装在任何循环中,对象只是坐在 space 中什么都不做。不要做 repeats:YES,那将是悲剧性的,因为您会随意地将计时器附加到 运行 循环,而没有指向它们的引用以知道如何或在何处停止它们。这通常是一件坏事。

为避免 EXC_BAD_ACCESS 异常,永远不要释放 NSTimer 将调用其方法的对象,如果该计时器尚未触发。您可能希望将待处理的 NSTimer 存储在 class 上的 属性 中,以便您可以处理此类事情。如果是 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.
    }
}