如何将计时器移动到后台线程

时间:2015-01-08 12:17:51

标签: ios swift grand-central-dispatch nstimer

我目前在我的应用程序中有两个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计时器,使其在后台线程中运行?我一直在阅读它,但一直感到困惑。

6 个答案:

答案 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)