延迟某个函数而不停止整个程序 // Swift 4



截至目前,我正在努力开发一个简单的点击游戏。我希望能够运行一个自动点击器,该自动点击器会自动将 X 次点击添加到您的总数中,每次之间有 1 秒的延迟。我的问题是,如果我尝试延迟和/或不运行自动点击器,它将冻结整个程序。我已经阅读了线程如何工作,但我并不完全了解如何在 Swift 4 中做到这一点。

这是我到目前为止所拥有的

@IBAction func auto_clicker_add(_ sender: Any)
{
    while auto_clicker_switch.isOn == true
    {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1)
        {
            self.count_taps = self.count_taps + 1
        }
    }
}

就像我对这个问题的回答一样,这里有一个替代解决方案而不是DispatchQueue

var timer: Timer?
@IBAction func auto_clicker_add(_ sender: Any) {
    timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateTimer), userInfo: nil, repeats: true)
}
@objc func updateTimer() {
    if (auto_clicker_switch.isOn == true) {
        self.count_taps += 1
    } else {
        timer.invalidate()
        timer = nil
    }
}

这在 Swift 中使用了Timer

附言虽然它与问题不是很相关,但您应该考虑在驼峰大小写而不是蛇大小写中重命名变量和函数。

我建议小心一点,以避免对视图控制器的强引用。

class ViewController: UIViewController {
    var tapCount = 0
    // I'd suggest weak reference to timer so that when it's invalidated, this is automatically set to `nil`
    weak var timer: Timer?
    // if view controller is dismissed, stop the timer (if it hasn't already been stopped)
    deinit {
        timer?.invalidate()
    }
    // switch to turn timer on or off has changed
    @IBAction func toggleTimer(_ sender: UISwitch) {
        timer?.invalidate()
        if sender.isOn {
            timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
                self?.tapCount += 1
            }
        }
    }
}

但关键点是:

  1. 基于块的计时器再现使您摆脱了令人困惑的target/selector语法。

  2. 当您使用 target/selector 方法时,正在运行的计时器会保留对target的强引用,这意味着如果您关闭视图控制器,计时器将继续运行,并且不会释放视图控制器。将计时器的基于闭包的格式副本与[weak self]模式结合使用可确保计时器不会保留对视图控制器的强引用。

  3. 由于计时器不再保留对视图控制器的强引用,我们现在可以使用deinit来确保如果视图控制器被关闭,计时器也将停止。

还有一个选项:

// a little bit more meaningful name
var autoClickTimer: Timer?
@IBAction func auto_clicker_add(_ sender: Any) {
  if auto_clicker_switch.isOn {
    // make sure that timer wasn't set yet
    guard self.autoClickTimer == nil else { return }
    self.autoClickTimer = setInterval(1) { [unowned self] in
      self.count_taps += 1
    }
  } else {
    self.autoClickTimer?.invalidate()
    self.autoClickTimer = nil
  }
}
// helper function (credits: http://peatiscoding.me/uncategorized/javascript-settimeout-swift-3-0/)
func setInterval(_ interval: TimeInterval, execute: @escaping () -> Void) -> Timer {
  return Timer.scheduledTimer(timeInterval: interval, target: BlockOperation(block: execute), selector: #selector(Operation.main), userInfo: nil, repeats: true)
}

最新更新