【问题标题】:NSTimer with anonymous function / block?具有匿名函数/块的 NSTimer?
【发布时间】:2013-02-02 05:02:37
【问题描述】:

我希望将来能够安排三个小事件,而不必为每个事件编写函数。我怎样才能使用NSTimer 做到这一点?我知道块有助于匿名函数,但它们可以在 NSTimer 中使用吗?如果可以,如何使用?

[NSTimer scheduledTimerWithTimeInterval:gameInterval  
         target:self selector:@selector(/* I simply want to update a label here */) 
         userInfo:nil repeats:NO];

【问题讨论】:

  • 你为什么不使用dispatch_after()?那是一个 GCD 函数,并以一个块作为参数。
  • 从来没有听说过...我如何使用它?只要我能说“等待 X 秒然后执行此操作”,我就很高兴!
  • 你可以试试看这里(NSTimer + Blocks)github.com/pandamonia/BlocksKit/blob/master/BlocksKit/… 或者,一般来说这里:github.com/pandamonia/BlocksKit
  • –performSelector:withObject:afterDelay: 方法呢?
  • @MartinR 并非所有 NSTimer 实例都需要使用 dispatch_after。例如,如果您希望特别能够阻止定时事件的发生,那么在调度块中设置标志并不总是等效的。如果我有一个在用户交互时触发的异步函数,并且我想等到他们完成交互以触发有效负载,那么 dispatch_after 它将在每次交互时触发一次,因为计时器可能会始终无效并且甚至永远不会尝试开火...节省大量内存。

标签: iphone objective-c nstimer


【解决方案1】:

如果你想实现类似于 NSTimer 和阻塞执行的东西,你可以使用 dispatch_after。

这是相同的示例代码:

    int64_t delayInSeconds = gameInterval; // Your Game Interval as mentioned above by you

    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

        // Update your label here. 

    });

希望这会有所帮助。

【讨论】:

  • 注意!您已经使用 int64_t 作为延迟秒数,因此这只适用于整数秒!我猜 gameInterval 不到 1 秒,所以这不会做你想要的。 (这个的 Xcode 模板非常具有欺骗性。)
  • 杰西是对的。请注意,实际参数接受以 纳秒 为单位测量的值,因此是 NSEC_PER_SEC 常量。所以你当然可以指定几分之一秒,但你必须小心你的数字类型。像(int64_t)(gameInterval * (double)NSEC_PER_SEC) 这样gameInterval 是double 的东西应该可以工作。
  • 该帖子询问如何使用 NSTimer 执行此操作。我不能使用 dispatch_after 使之无效。
【解决方案2】:

你实际上可以调用:

NSTimer.scheduledTimerWithTimeInterval(ti: NSTimeInterval,
                    target: AnyObject, 
                    selector: #Selector, 
                    userInfo: AnyObject?, 
                    repeats: Bool)

像这样使用它:

NSTimer.scheduledTimerWithTimeInterval(1, 
                    target: NSBlockOperation(block: {...}), 
                    selector: #selector(NSOperation.main), 
                    userInfo: nil, 
                    repeats: true)

【讨论】:

  • 太棒了。谢谢。
  • 快速简单。效果很好。谢谢!
  • 这应该是选择的答案,因为它允许计时器失效,而接受的答案则不允许。
【解决方案3】:

A block based timer API exists in Cocoa(从 iOS 10+ / macOS 10.12+ 开始)- 从 Swift 3 开始,您可以使用以下方法:

Timer(timeInterval: gameInterval, repeats: false) { _ in
    print("herp derp")
}

… 或在 Objective-C 中:

[NSTimer scheduledTimerWithTimeInterval:gameInterval repeats:NO block:^(NSTimer *timer) {
    NSLog(@"herp derp");
}];

如评论中所述,请注意不要在块内使用强自引用以避免保留循环 (for more info)。

如果您需要定位早于 iOS10、macOS 12、tvOS 10、watchOS 3 的操作系统版本,您应该使用其他解决方案之一。

【讨论】:

  • 我很高兴最近发现了这个新增功能。鉴于我们从 iOS 4 开始就有块,Apple 直到 iOS 10 才为 NSTimer 提供基于块的 API,这有点令人惊讶!
  • 令人惊讶的是,这对我不起作用,至少在 Objective-C 中不起作用。该块根本不会被调用。超时不是问题,因为常规的 NSTimer 初始化使用相同的超时。持有 NSTimer 变量也不是问题,因为即使我强烈持有它也不起作用。
  • 我向你保证 Cocoa API 可以工作。您可能在主线程(或另一个具有适当配置的运行循环的线程)之外调用它。
  • 很高兴我不是唯一一个记录“herp derp”来测试的人。
  • 确保仅在其中使用弱引用,否则您将有一个保留周期。
【解决方案4】:

@Peter Peng 答案的 Objective-C 版本:

_actionDelayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Well this is useless.");
}] selector:@selector(main) userInfo:nil repeats:YES];

【讨论】:

    【解决方案5】:

    这很简单,但它不包含在 Apple 框架中,至少目前还没有。

    您可以自己为NSTimer 编写一个基于块的包装器,例如使用GCD,或者您可以使用现有的第三方库,例如:https://github.com/jivadevoe/NSTimer-Blocks。

    【讨论】:

      【解决方案6】:

      我在 NSTimer 上创建了一个类别,可以将其与块一起使用。

      https://github.com/mBrissman/NSTimer-Block

      【讨论】:

        【解决方案7】:

        截至 2018 年底,您完全可以这样做:

        Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { timer in
          print("no, seriously, this works on iPhone")
        } 
        

        感谢@JohnnyC!

        真的很奇怪!

        【讨论】:

          【解决方案8】:

          我喜欢这个技巧@Peter-Pang! BlockOperation 是动态创建的,由运行队列拥有的 Timer 拥有,并调用块上的主选择器来运行它......很好!!!

          为Swift 3更新

          Timer.scheduledTimer(timeInterval: 1, target: BlockOperation { // ... }, selector: #selector(Operation.main), userInfo: nil, repeats: false)

          【讨论】:

            【解决方案9】:

            我已经制作了一个宏来执行此操作并自动处理 dealloc、arc、retain 循环等。无需担心使用弱引用。它也将始终在主线程上

            #define inlineTimer(__interval, __block) {\
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((0.0) * NSEC_PER_SEC)), dispatch_get_main_queue(), (__block));\
            [NSTimer scheduledTimerWithTimeInterval:__interval repeats:YES block:^(NSTimer *__timer) {\
            if (self.window != nil) {\
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((0.0) * NSEC_PER_SEC)), dispatch_get_main_queue(), (__block));\
            } else {\
            [__timer invalidate];\
            }\
            }];\
            }
            

            示例用法:

            __block ticks = 0;
            
            inlineTimer(0.5, ^{
                ticks++;
                self.label.text = [NSString stringWithFormat:@"Ticks: %i", ticks];//strong reference to self and self.label won't cause retain cycle! Wahoo
            });
            

            请注意 self.label 不是弱引用,但由于宏的结构,这一切都会自动释放。

            当然,这仅适用于 UI 类,因为它正在检查 .window 以了解何时应该解除分配。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-05-19
              • 2019-01-15
              相关资源
              最近更新 更多