我目前在我的应用程序中有两个NSTimer计时器,负责获取数据和更新UI。我最近注意到,虽然计时器正在运行但我的UI性能很差,例如用户无法很好地滚动浏览我的UITableview。我在其他地方读过,可以将定时器推入不同的runloop,这可能会有所帮助。这就是我的计时器现在的样子:
let aSelector : Selector = "updateLocation"
timer = NSTimer.scheduledTimerWithTimeInterval(((NSUserDefaults.standardUserDefaults().stringForKey("mapUpdate")! as NSString).doubleValue), target: self, selector: aSelector, userInfo: nil, repeats: true)
如何修改我的Swift计时器,使其在后台线程中运行?我一直在阅读它,但一直感到困惑。
答案 0 :(得分:5)
如果在主线程的运行循环上触发计时器,则UI或计时器可能会出现问题。
例如,假设您触发了一个计时器。您的应用程序中还有一个tableView,它向用户显示一个列表,并且用户正在滚动表。同时,计时器的时间过去了。我们希望计时器的代码块可以立即执行,但是直到用户结束滚动操作才执行。在很多情况下,这不是我们想要的。
实际上,此问题的源头以及您在问题中提到的问题是,您已经在主线程上运行了计时器,在该线程上,您的任务和所有UI操作都是按顺序处理的。串行表示仅在上一个任务完成时才执行一个任务。
为解决这个问题,一种解决方案是在后台线程的运行循环上调用计时器,如下所示:
DispatchQueue.global(qos: .background).async {
let timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: selector(fireTimer), repeats: false)
let runLoop = RunLoop.current
runLoop.add(timer, forMode: .defaultRunLoopMode)
runLoop.run()
}
}
注意:如果您的计时器被重复调用,请不要忘记在任务结束时调用计时器的invalidate(),否则运行循环将强烈引用计时器的目标对象,这可能会导致内存泄漏。
还有一个更好的选择,而不是Timer类。您可以使用DispatchSourceTimer。
有关更多信息,请参见以下链接:
答案 1 :(得分:1)
要在不同的运行循环上安排计时器,您可以使用:
// Create an unscheduled timer
let timer = NSTimer(
((NSUserDefaults.standardUserDefaults().stringForKey("mapUpdate")! as NSString).doubleValue),
target: self,
selector: "updateLocation",
userInfo: nil,
repeats: true)
// Add the timer to a runloop (in this case the main run loop)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
请注意,我不确定这是否真的可以解决您的问题,甚至可能是处理问题的好方法。从您的进一步讨论开始,我认为我可能会关注使用CLLocationManager.startMonitoringSignificantLocationChanges
的内容,这样就可以根据需要定期检查位置,只需更新显着位置,并在适当时明确定义。请注意,速度和课程可直接从CLLocation
获取,因此以精确的时间间隔更新并不重要。
答案 2 :(得分:0)
用于在后台触发计时器:read this blog
您也可以定期使计时器运行,然后让目标方法在后台执行繁重的操作。
func updateLocation(){
// Do the heavy lifting in Background thread
// Take main thread and reload UI
}
答案 3 :(得分:0)
Swift 2:
Here是一个在后台线程上运行的计时器类,所以假设你正在使用它,就像这样:
let yourTimer = JBTimer()
myTimer.repeateTimer(timeInSecs: 2) {
// Proper way to update UI from background threads.
dispatch_async(dispatch_get_main_queue()) {[unowned self] in
// Update UI stuff here.
}
}
答案 4 :(得分:0)
Swift 4.2:
RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
答案 5 :(得分:-1)
在swift5中更新
RunLoop.main.add(timer, forMode: RunLoop.Mode.common)