【问题标题】:Using an NSTimer in Swift在 Swift 中使用 NSTimer
【发布时间】:2014-08-13 17:02:58
【问题描述】:

在这种情况下,永远不会调用 timerFunc()。我错过了什么?

class AppDelegate: NSObject, NSApplicationDelegate {

    var myTimer: NSTimer? = nil

    func timerFunc() {
        println("timerFunc()")
    }

    func applicationDidFinishLaunching(aNotification: NSNotification?) {
        myTimer = NSTimer(timeInterval: 5.0, target: self, selector:"timerFunc", userInfo: nil, repeats: true)
    }
}

【问题讨论】:

  • 如果你使用定时器的init你必须You must add the new timer to a run loop, using addTimer:forMode:。这是文档描述中的第二句话。否则使用scheduledTimerWithTimeInterval,这可能就是您要查找的内容。
  • 为什么考虑到几个人提供了不同的答案而投反对票?
  • 不能肯定地告诉你,但可能是因为答案并不难找到,正如我之前指出的那样。它在文档中是正确的。如果您让option 点击您的方法,您将在 5 秒内找到解决方案,甚至无需离开 Xcode。

标签: swift nstimer


【解决方案1】:

您可以创建一个定时定时器,它会自动将自身添加到运行循环并开始触发:

斯威夫特 2

NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "timerDidFire:", userInfo: userInfo, repeats: true)

斯威夫特 3、4、5

Timer.scheduledTimer(withTimeInterval: 0.5, target: self, selector: #selector(timerDidFire(_:)), userInfo: userInfo, repeats: true)

或者,您可以保留当前代码,并在准备好时将计时器添加到运行循环:

斯威夫特 2

let myTimer = NSTimer(timeInterval: 0.5, target: self, selector: "timerDidFire:", userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(myTimer, forMode: NSRunLoopCommonModes)

斯威夫特 3、4、5

let myTimer = Timer(timeInterval: 0.5, target: self, selector: #selector(timerDidFire(_:)), userInfo: nil, repeats: true)
RunLoop.current.add(myTimer, forMode: RunLoop.Mode.common)

【讨论】:

  • 如何暂停和恢复这个计时器?
  • myTimer.invalidate() 取消它。除此之外,NSTimer 不支持任何暂停/恢复功能。您可以想象一个简单的子类,它通过 (1) 记录开始时间、(2) 记录暂停时间和 (3) 在恢复时添加这种支持,运行时间比我们原来的 timeInterval 更短的 t2-t1。
  • Sift 3.0 的运行循环语法:RunLoop.current.add(myTimer, forMode: RunLoopMode.commonModes)
  • Swift3 RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
【解决方案2】:

我使用与卢克类似的方法。 仅对“私人方法”纯粹主义者的警告:

不要在 Swift 中将回调设为私有。

如果你写:

private func timerCallBack(timer: NSTimer){

..

你会得到:

timerCallBack:]: 无法识别的选择器发送到实例...由于未捕获的异常“NSInvalidArgumentException”而终止应用程序

【讨论】:

  • 非常感谢!
  • 只需将@objc 添加到私有方法中,它也可以工作:@objc private func timerCallBack(timer: NSTimer){
  • @Lensflare 的建议答案是正确的,你只需要 @objc 方法装饰器。
  • 是的,我知道“@objc”,但我们说的是“swift”纯粹主义者......所以避免来自 objc 的所有旧交易。 (我也喜欢它..)
【解决方案3】:

除非您使用NSTimer.scheduledTimerWithTimeInterval,否则不会自动安排NSTimer:

myTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "timerFunc", userInfo: nil, repeats: true)

【讨论】:

    【解决方案4】:

    正如 Drewag 和 Ryan 指出的那样,您需要创建一个预定的计时器(或自己安排)。最简单的方法是使用以下方法创建它:

    myTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "timerFunc:", userInfo: nil, repeats: true)
    

    您还需要更改 timerFunc(以及相关的选择器)的定义以获取参数并以“:”结尾

    func timerFunc(timer:NSTimer!) {
        ...
    }
    

    【讨论】:

    • 你不需要定义它来接受一个参数。无论哪种方式都可以。
    • @drewag 好的,我只记得关于 SO 的一些讨论,如果没有 ':',它就无法工作,所以我在操场上试了一下。如果您(正确地)将回调定义为带参数,则需要“:”。 “正确”,因为NSTimer 文档说回调必须采用单个参数。 “选择器应具有以下签名:timerFireMethod:(包括一个冒号,表示该方法需要一个参数)。”似乎您可以在没有 : 或参数的情况下逃脱,但我仍然会说根据文档我的答案是正确的 :)
    • 这些参数在 IBActions 和我曾经定义过选择器的任何其他地方也是可选的(至少只有一个参数)。这只是运行时的本质。他忽略了论点的事实与为什么不调用它无关。最坏的情况是它会抛出一个关于找不到选择器的异常,而不是静默失败。
    【解决方案5】:

    对于 Swift 3

    var timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(ViewController.updateTimer), userInfo: nil, repeats: true);
    RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
    

    【讨论】:

    • 使用scheduledTimer()方法会自动将你的Timer添加到runloop——你不需要手动添加。
    【解决方案6】:

    运行循环的 Swift 3.0 语法:

    RunLoop.current.add(myTimer, forMode: .commonModes)
    

    【讨论】:

      【解决方案7】:

      这是一段代码,演示了如何在不带参数的情况下使用 AND 调用函数(延迟)。

      在 xCode (singleViewApplication) 的新项目中使用它,并将代码放入标准 viewController:

      class ViewController: UIViewController {
      
          override func viewDidLoad() {
      
              super.viewDidLoad()
      
              NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: Selector("delayedFunctionWithoutParameter:"), userInfo: nil, repeats: false)
      
              let myParameter = "ParameterStringOrAnyOtherObject"
      
              NSTimer.scheduledTimerWithTimeInterval(4.0, target: self, selector: Selector("delayedFunctionWithParameter:"), userInfo: myParameter, repeats: false)
          }
      
          // SIMPLE TIMER - Delayed Function Call
          func delayedFunctionWithoutParameter(timer : NSTimer) {
              print("This is a simple function beeing called without a parameter passed")
              timer.invalidate()
          }
      
          // ADVANCED TIMER - Delayed Function Call with a Parameter
          func delayedFunctionWithParameter(timer : NSTimer) {
      
              // check, wether a valid Object did come over
              if let myUserInfo: AnyObject = timer.userInfo {
                  // alternatively, assuming it is a String for sure coming over
                  // if let myUserInfo: String = timer.userInfo as? String {
                  // assuming it is a string comming over
                  print("This is an advanced function beeing called with a parameter (in this case: \(myUserInfo)) passed")
              }
      
              timer.invalidate()
          }
      }
      

      注意,在任何情况下,您都应该使用参数 (timer : NSTimer) 实现延迟函数,以便能够使计时器无效(终止、结束)。通过 passend “计时器”,您还可以访问 userInfo(您可以在其中放置任何对象,不仅是字符串对象,还可以放置数组和字典等集合类型)。

      原始 Apples 文档说“” -> 计时器将自身作为参数传递,因此该方法将采用以下模式: - (void)timerFireMethod:(NSTimer *)timer 完整阅读 -> here

      【讨论】:

        【解决方案8】:

        由于这个帖子让我自己尝试将计时器放在 RunLoop 上(这解决了我的问题),我也发布了我的具体案例 - 谁知道它可能对某人有所帮助。 我的计时器是在应用程序启动和所有对象的初始化期间创建的。我的问题是,虽然它确实安排了计时器,但它仍然没有触发。我的猜测是,之所以出现这种情况,是因为scheduledTimerWithTimeInterval 在应用程序启动期间将计时器置于不同的 RunLoop 上。如果我只是初始化计时器然后使用NSRunLoop.mainRunLoop().addTimer(myTimer, forMode:NSDefaultRunLoopMode),它工作正常。

        【讨论】:

          【解决方案9】:

          使用 swift3,你可以运行它,

          var timer: Timer?
          func startTimer() {
          
              if timer == nil {
                  timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.loop), userInfo: nil, repeats: true)
              }
          }
          
          func stopTimer() {
              if timer != nil {
                  timer?.invalidate()
                  timer = nil
              }
          }
          
          func loop() {
              //do something
          }
          

          【讨论】:

            【解决方案10】:

            要使用 OP 建议的方法,您需要将其添加到运行循环中:

            myTimer = NSTimer(timeInterval: 5.0, target: self, selector:"timerFunc", userInfo: nil, repeats: true)
            NSRunLoop.mainRunLoop().addTimer(myTimer, forMode:NSDefaultRunLoopMode)
            

            文档还说目标应该接受一个参数,但没有它它也可以工作。

            func timerFireMethod(timer: NSTimer) { }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-03-15
              • 1970-01-01
              • 2015-05-22
              • 1970-01-01
              相关资源
              最近更新 更多