在Swift中每隔X分钟执行一次操作


Answers:


171
var helloWorldTimer = NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: Selector("sayHello"), userInfo: nil, repeats: true)

func sayHello() 
{
    NSLog("hello World")
}

记住要导入基金会。

斯威夫特4:

 var helloWorldTimer = Timer.scheduledTimer(timeInterval: 60.0, target: self, selector: #selector(ViewController.sayHello), userInfo: nil, repeats: true)

 @objc func sayHello() 
 {
     NSLog("hello World")
 }

1
但是如果您想在视图之间切换怎么办?代码会停止吗?
2013年

5
@Cing取决于自我指的是什么。
Antzi '16

1
不要忘了,NSTimer保留了它的目标,因此,在此设置,如果helloWorldTimer是在一个属性self你拥有属于自己的保留周期,在self保留helloWorldTimerhelloWorldTimer保留self
MANIAK_dobrii

@MANIAK_dobrii您还能就如何打破保留周期发表评论吗?如果取消了VC但未取消计时器,则该循环是否还完好?因此,您必须同时取消计时器,然后关闭视图以中断周期吗?
Dave G

1
对于Swift 4,要获取选择器的方法必须公开给Objective-C,因此必须将@objc属性添加到方法声明中。例如```@objc func sayHello(){}```
Shamim Hossain

136

如果定位到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通常最好,但是为了完整起见,我应该注意您也可以使用调度计时器,这对于在后台线程上调度计时器很有用。使用调度计时器,由于它们是基于块的,因此只要您使用引用,它就可以避免使用旧的target/ selector模式的强大的引用周期挑战。Timerweak

所以:

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?.cancel()
    timer = nil
}

deinit {
    self.stopTimer()
}

有关更多信息,请参见《并发编程指南》中“ 派遣源”部分中“ 派遣源示例 ”的“ 创建计时器”部分


对于Swift 2,请参阅此答案的先前版本


在这种方法中,您将如何选择只执行一次的计时器?
Juan Boero

@JuanPabloBoero-我不会在发生一次火灾的情况下使用此方法。你会用dispatch_after。还是非重复的NSTimer
罗布

@Rob我在屏幕上有一个计数器标签,该标签每秒钟从一个函数中更新一次,并且我正在使用上面的代码来增加计数器并更新标签文本。但是,我面临的问题是我的计数器变量每秒更新一次,但是屏幕没有在UILabel中显示最新的计数器值。
Nagendra Rao

饶,以上对于在后台线程上执行某些操作很有用。但是您的UI更新必须在主队列上进行。因此,可以在主线程上安排此时间,或者至少将UI更新分派回主线程。
罗布

1
这个问题不属于此答案的注释。在上方删除您的评论并发表您自己的问题。但是,简而言之,您通常实际上并不使应用程序在后台运行,而只是使它看起来像是在后台。请参阅stackoverflow.com/a/31642036/1271826
罗布

17

这是使用闭包而不是命名函数的NSTimerSwift 3(NSTimer已重命名为Timer)的答案的更新:

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

6
仅适用于iOS 10。
Glenn

请不要发布ios 10+解决方案
Numan Karaaslan,

13

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

private func executeRepeatedly() {
    // put your code here

    DispatchQueue.main.asyncAfter(deadline: .now() + 60.0) { [weak self] in
        self?.executeRepeatedly()
    }
}

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


这个决定比拥有所有这些计时器,将它们添加到运行循环,使它们无效……更加方便。
julia_v

这似乎是有效的解决方案,但是当我将其与Web服务调用一起使用时,它正在创建一个保留周期……
jayant rawat

11

您可以使用Timer(速记3)

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

在selector()中,输入函数名称


如何在Swift 3中做到这一点?
bibscy

1
使用Timer... NSTimer已重命名
约瑟夫

7

在快速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刷新率同步。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.