延迟某个功能而不停止整个程序// Swift 4

问题描述 投票:1回答:3

截至目前,我正致力于开发一款简单的点击游戏。我希望能够运行一个自动点击器,自动将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
        }
    }
}
ios swift multithreading delay
3个回答
0
投票

我建议稍加注意避免强烈引用视图控制器。

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来确保如果视图控制器被解除,那么计时器也将被停止。

2
投票

就像我对this question的回答一样,这里是替代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

附:虽然它与问题没有多大关系,但您应该考虑在驼峰情况下重命名变量和函数而不是蛇形。


0
投票

还有一个选择:

// 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)
}
© www.soinside.com 2019 - 2024. All rights reserved.