【问题标题】:Objective C for loop delayObjective C for 循环延迟
【发布时间】:2015-06-17 00:19:34
【问题描述】:

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

【问题讨论】:

  • 使用NSTimer 也是不错的解决方案。
  • @zaph NSTimer 不能与 cocos2d 或 SpriteKit 一起使用。更喜欢 CCNode 及其所有派生类提供的内置 schedule 方法。

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


【解决方案1】:

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

                });
}

【讨论】:

    【解决方案2】:

    @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 是否被保留。如果不是这种情况,您应该将对它的引用存储为管理所有这些的任何类的属性。

    还值得注意的是,由于 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.
        }
    }
    

    【讨论】:

    • 感谢您使用计时器提供不同的解决方案并很好地解释它。我很感激。
    • 当然!深入回答问题是确保我保持敏锐度的好方法,因此我认为这是双赢的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多