在Swift中进行轮询的正确方法是什么?

10

我有很多其他编程语言的经验,但在Swift 3方面并不是很熟悉。我想要做一个轮询循环。这是我写的代码:

DispatchQueue.global(qos: .userInitiated).async {
            [unowned self] in
            while self.isRunning {
                WebService.getPeople(completion: nil)
                sleep(100)
            }
        }

这种方法对我来说很好用,每100秒进行一次轮询,然后让这个线程休眠。我的疑问是,在swift 3中这样做是否正确?


1
首先,如果可以的话最好不要使用定时器。但是如果必须使用,只需简单地使用一个“计时器”。 - Paulw11
1
通常情况下,如果可以避免的话,请不要在分派线程上长时间阻塞。 - JeremyP
@JeremyP 我听别人说过完全相同的话,但我不明白为什么?如果我在一些低优先级的后台线程上进行调度,会引起什么问题? - MegaManX
使用GCD,队列上的任务会从池中分配线程,该池是由操作系统根据诸如CPU数量之类的因素计算出来的。当原始任务处于睡眠状态时,线程无法重新分配到另一个调度队列任务。 - JeremyP
3个回答

15

你有两个选择:

  • 使用 NSTimer
  • 使用 DispatchSourceTimer

使用 NSTimer 相对简单,但需要一个活动的运行循环。因此,如果你需要在后台线程上进行轮询,可能会有一些麻烦,因为你需要创建一个线程并在其中保持一个运行循环(也许是定时器本身将保持运行循环)。
另一方面,DispatchSourceTimer 使用队列工作。你可以轻松地从系统提供的队列之一创建调度源定时器或者自己创建一个。

    var timer: DispatchSourceTimer?
    let queue = DispatchQueue.global(qos: .background)
    guard let timer = DispatchSource.makeTimerSource(queue: queue) else { return }
    timer.scheduleRepeating(deadline: .now(), interval: .seconds(100), leeway: .seconds(1))
    timer.setEventHandler(handler: { 
        // Your code
    })
    timer.resume()

leeway 参数是系统可以推迟计时器的时间量。


3

Swift 4 和 Swift 5


 var timer: Timer? //declare outside function scope
 var runCount = 0


 self.timer = Timer(timeInterval: 2.0, target: self, selector: #selector(self.fireTimer), userInfo: nil, repeats: true)

 guard let timer = self.timer else {return}
 RunLoop.main.add(self.timer, forMode: RunLoop.Mode.default)

 @objc func fireTimer() {
         print("Timer fired! \(runCount)")
        runCount += 1

        if runCount == 5 {
            timer?.invalidate() //stop the timer
        }
    }

OR

self.timer = Timer.init(timeInterval: 1.0, repeats: true, block: { (timer) in
        print("\n--------------------TIMER FIRED--------------\n")
        runCount += 1
        if runCount == 5{
          timer.invalidate()
    }
    })
guard let timer = self.timer else {return}

RunLoop.main.add(self.timer!, forMode: RunLoopMode.defaultRunLoopMode)

3

Swift 5,iOS 10.0+

接受的答案中的代码已不再编译,可以进行修改(并简化!)如下:

DispatchQueue.global(qos: .userInitiated).async {
    let timer = Timer.scheduledTimer(withTimeInterval: 100, repeats: true) { timer in
        // Your action
    }
    timer.fire()
}

3
我在我的应用程序中尝试过这个,但计时器只触发了一次,再也没有触发了... - Mark McGookin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接