【问题标题】:Do something every x minutes in Swift在 Swift 中每 x 分钟做一次
【发布时间】:2014-11-15 02:45:06
【问题描述】:

如何每分钟运行一个函数? 在 JavaScript 中我可以做类似setInterval 的事情,在 Swift 中是否存在类似的事情?

想要的输出:

Hello World 每分钟一次...

【问题讨论】:

标签: ios swift setinterval intervals repeat


【解决方案1】:

这是另一个版本algrid's 回答,有一个简单的方法来阻止它

@objc func executeRepeatedly() {

    print("--Do something on repeat--")
        
    perform(#selector(executeRepeatedly), with: nil, afterDelay: 60.0)
}

这是一个如何启动和停止它的示例:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    executeRepeatedly() // start it
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    NSObject.cancelPreviousPerformRequests(withTarget: self) // stop it
}

【讨论】:

    【解决方案2】:

    如果针对 iOS 10 及更高版本,您可以使用基于块的 Timer 再现,这简化了潜在的强引用周期,例如:

    weak var timer: Timer?
    
    func startTimer() {
        timer?.invalidate()   // just in case you had existing `Timer`, `invalidate` it before we lose our reference to it
        timer = Timer.scheduledTimer(withTimeInterval: 60.0, repeats: true) { [weak self] _ in
            // do something here
        }
    }
    
    func stopTimer() {
        timer?.invalidate()
    }
    
    // if appropriate, make sure to stop your timer in `deinit`
    
    deinit {
        stopTimer()
    }
    

    虽然Timer 通常是最好的,但为了完整起见,我应该注意您还可以使用调度计时器,这对于在后台线程上调度计时器很有用。使用调度计时器,由于它们是基于块的,因此只要您使用 weak 引用,就可以避免使用旧的 target/selector 模式 Timer 带来的一些强大的引用周期挑战。

    所以:

    var timer: DispatchSourceTimer?
    
    func startTimer() {
        let queue = DispatchQueue(label: "com.domain.app.timer")  // you can also use `DispatchQueue.main`, if you want
        timer = DispatchSource.makeTimerSource(queue: queue)
        timer!.schedule(deadline: .now(), repeating: .seconds(60))
        timer!.setEventHandler { [weak self] in
            // do whatever you want here
        }
        timer!.resume()
    }
    
    func stopTimer() {
        timer = nil
    }
    

    有关详细信息,请参阅并发编程指南的Dispatch Sources 部分中的调度源示例创建计时器部分。


    对于 Swift 2,请参阅 previous revision of this answer

    【讨论】:

    • 在这种方法中你会如何使用只执行一次的计时器?
    • @JuanPabloBoero - 我不会将这种方法用于一次性的情况。你会使用dispatch_after。或不重复的NSTimer
    • @Rob 我在屏幕上有一个计数器标签,它每秒都会从一个函数中更新,我正在使用上面的代码来增加我的计数器并更新标签文本。但是,我面临的问题是我的计数器变量每秒更新一次,但屏幕没有在我的 UILabel 中显示最新的计数器值。
    • Rao,以上对于在后台线程上执行某些操作很有用。但是您的 UI 更新必须发生在主队列上。因此,要么将其安排在主线程上,要么至少将 UI 更新分派回主线程。
    • 这个问题在 cmets 中不属于这个答案。删除上面的 cmets 并发布您自己的问题。但是,简而言之,您通常不会真正让应用程序在后台运行,而只是让它看起来像原来那样。见stackoverflow.com/a/31642036/1271826
    【解决方案3】:

    你可以使用Timer (swift 3)

    var timer = Timer.scheduledTimerWithTimeInterval(60, target: self, selector: Selector("function"), userInfo: nil, repeats: true)
    

    在 selector() 中输入你的函数名

    【讨论】:

    • 如何在 swift 3 中做到这一点?
    • 使用Timer...NSTimer已重命名
    【解决方案4】:
    var helloWorldTimer = NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: Selector("sayHello"), userInfo: nil, repeats: true)
    
    func sayHello() 
    {
        NSLog("hello World")
    }
    

    记得导入 Foundation。

    斯威夫特 4:

     var helloWorldTimer = Timer.scheduledTimer(timeInterval: 60.0, target: self, selector: #selector(ViewController.sayHello), userInfo: nil, repeats: true)
    
     @objc func sayHello() 
     {
         NSLog("hello World")
     }
    

    【讨论】:

    • 但是如果你想在视图之间切换呢?代码会停止吧?
    • @Cing 取决于 self 指的是什么。
    • 不要忘记 NSTimer 保留它的目标,因此,使用此设置,如果 helloWorldTimerself 上的一个属性,您就有了一个保留周期,其中 self保留helloWorldTimerhelloWorldTimer 保留self
    • @MANIAK_dobrii 你能否评论一下如何打破保留周期?如果 VC 被解散但计时器没有被取消,那么循环是否仍然有效?所以你必须取消计时器然后关闭视图才能打破循环?
    • 对于 Swift 4,要获取选择器的方法必须暴露给 Objective-C,因此必须在方法声明中添加 @objc 属性。例如 ``` @objc func sayHello(){} ```
    【解决方案5】:

    如果您可以允许一段时间漂移,这里有一个简单的解决方案,每分钟执行一些代码:

    private func executeRepeatedly() {
        // put your code here
    
        DispatchQueue.main.asyncAfter(deadline: .now() + 60.0) { [weak self] in
            self?.executeRepeatedly()
        }
    }
    

    只需运行一次executeRepeatedly(),它将每分钟执行一次。当拥有对象 (self) 被释放时,执行停止。您还可以使用标志来指示必须停止执行。

    【讨论】:

    • 这个决定比围绕所有这些计时器更方便,将它们添加到运行循环中,使它们无效......酷!
    • 这似乎是有效的解决方案,但是当我将它与我的 Web 服务调用一起使用时,它会创建一个保留周期......
    【解决方案6】:

    这里是 NSTimer 答案的更新,用于 Swift 3(其中 NSTimer 被重命名为 Timer)使用闭包而不是命名函数:

    var timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) {
        (_) in
        print("Hello world")
    }
    

    【讨论】:

    • 这仅适用于 iOS 10。
    • 请不要发布ios 10+解决方案
    【解决方案7】:

    在 swift 3.0 中,GCD 进行了重构:

    let timer : DispatchSourceTimer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
    
    timer.scheduleRepeating(deadline: .now(), interval: .seconds(60))
    timer.setEventHandler
    {
        NSLog("Hello World")
    }
    timer.resume()
    

    当您需要在特定队列上调度时,这特别有用。另外,如果您打算使用它来更新用户界面,我建议您查看CADisplayLink,因为它与 GPU 刷新率同步。

    【讨论】:

      猜你喜欢
      • 2019-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-02
      • 2016-03-28
      相关资源
      最近更新 更多