如何每分钟运行一次函数?在JavaScript中,我可以使用类似setInterval
的东西,Swift中是否存在类似的功能呢?
期望的输出:
每分钟运行一次“Hello World”...
如何每分钟运行一次函数?在JavaScript中,我可以使用类似setInterval
的东西,Swift中是否存在类似的功能呢?
期望的输出:
每分钟运行一次“Hello World”...
var helloWorldTimer = NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: Selector("sayHello"), userInfo: nil, repeats: true)
func sayHello()
{
NSLog("hello World")
}
记得导入Foundation库。
Swift 4:
var helloWorldTimer = Timer.scheduledTimer(timeInterval: 60.0, target: self, selector: #selector(ViewController.sayHello), userInfo: nil, repeats: true)
@objc func sayHello()
{
NSLog("hello World")
}
NSTimer
会保留它的目标对象,因此,在这种设置下,如果 helloWorldTimer
是 self
的属性,则会出现保留循环,其中 self
保留 helloWorldTimer
,而 helloWorldTimer
保留 self
。 - MANIAK_dobriitimer
持有 VC
,而 VC
又持有 timer
时,循环将一直保持,直到其中一个释放另一个,这就是引用计数。最好的方法是 -invalidate
定时器,这将释放它的目标并打破循环。当你完成使用定时器时,你需要 -invalidate
它,但不要在 VC
的 -dealloc
中这样做,因为 dealloc 在对象最后一次释放后被调用,你知道,在那里你需要打破一个保留循环。 - MANIAK_dobrii@objc func sayHello(){}
。 - Shamim Hossain如果针对 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
,但为了完整起见,我应该注意到您也可以使用分派计时器,这对于在后台线程上安排计时器很有用。使用分派定时器,由于它们是基于块的,所以避免了旧的Timer
的target
/selector
模式中一些强引用循环挑战,只要您使用weak
引用。
所以:
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
}
了解更多信息,请参见 Concurrency Programming Guide 中的 Dispatch Source Examples 的 Creating a Timer 部分。在 Dispatch Sources 部分。
对于 Swift 2,请参见这个答案的之前的版本。
dispatch_after
或者一个非重复的NSTimer
。 - Rob如果您可以容许一些时间漂移,这里有一个简单的解决方案,每分钟执行一些代码:
private func executeRepeatedly() {
// put your code here
DispatchQueue.main.asyncAfter(deadline: .now() + 60.0) { [weak self] in
self?.executeRepeatedly()
}
}
只需运行executeRepeatedly()
一次,它就会每分钟执行一次。当拥有对象(self
)被释放时,执行将停止。您还可以使用标志来指示执行必须停止。
以下是使用闭包而不是命名函数来更新 NSTimer
答案的Swift 3版本(其中NSTimer
已重命名为Timer
):
var timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) {
(_) in
print("Hello world")
}
Timer
(Swift 3)var timer = Timer.scheduledTimerWithTimeInterval(60, target: self, selector: Selector("function"), userInfo: nil, repeats: true)
在selector()函数中,你需要输入你的函数名。Timer
... NSTimer
已更名。 - Joseph在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()
这对于需要在特定队列上执行操作时非常有用。此外,如果您计划将其用于用户界面更新,则建议查看与GPU刷新率同步的CADisplayLink
。
你可以像这样简单地执行一个for循环:
for try await tick in Every(.seconds(3)) {
print(tick)
}
struct Every: AsyncSequence, AsyncIteratorProtocol {
typealias Element = Int
var duration: Duration
private(set) var tick = 0
init(_ duration: Duration) { self.duration = duration }
func makeAsyncIterator() -> Self { self }
mutating func next() async throws -> Element? {
try await Task.sleep(for: duration)
guard !Task.isCancelled else { return nil }
tick += 1
return tick
}
}
当然,你可以通过将整个操作包装在一个Task
中,并随时取消任务来异步执行它,就像这样:
let clock = Task {
for try await tick in Every(.seconds(3)) {
print(tick)
if tick == 3 { clock.cancel() } // Cancel the task after 3 ticks
}
}
在.task
修饰符中执行此操作将自动将迭代的生命周期绑定到View
的生命周期。无需担心无效化的问题:
.task {
do {
for try await tick in Every(.seconds(1)) { print(tick) }
} catch {
print(error)
}
}
timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true, block: myMethod)
func myMethod(_:Timer) {
...
}
或者
timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in
...
}
请确保在某个时刻使计时器无效,例如您的时间不再可见,或者您的对象已被销毁。
这是另一个版本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
}