截至目前,我正在努力开发一个简单的点击游戏。我希望能够运行一个自动点击器,该自动点击器会自动将 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
}
}
}
}
但关键点是:
基于块的计时器再现使您摆脱了令人困惑的
target
/selector
语法。当您使用
target
/selector
方法时,正在运行的计时器会保留对target
的强引用,这意味着如果您关闭视图控制器,计时器将继续运行,并且不会释放视图控制器。将计时器的基于闭包的格式副本与[weak self]
模式结合使用可确保计时器不会保留对视图控制器的强引用。由于计时器不再保留对视图控制器的强引用,我们现在可以使用
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)
}