Swift Timer.scheduledTimer()不起作用 [英] Swift Timer.scheduledTimer() doesn't work
问题描述
我的快速应用程序中有两个视图.我正在执行以下segue.
I have two views in my swift app. I am performing a segue as below.
ViewController.swift -----------------> GameViewController.swift
ViewController.swift -----------------> GameViewController.swift
在加载GameViewController时,还会将一个值数组从ViewController.swift传递给GameViewController.swift
When loading the GameViewController an value array also passed to GameViewController.swift from ViewController.swift
应该在GameViewController.swift中初始化计时器
A timer should be initialized in GameViewController.swift
我试图初始化一个计时器并通过它调用一个方法,但是它不起作用.
I tried to initialize a timer and call a method through it, but it doesn't work.
以下是我的代码段.
ViewController.swift
ViewController.swift
func signIn(difficultyLvl:String){
let username = usernameTxt.text
let password = passwordTxt.text
let url = URL(string: "http://192.168.1.106/speed/scoreBoardController.php?username="+username!+"&password="+password!+"&action=SIGNIN")
let task = URLSession.shared.dataTask(with: url!) {(data, response, error) in
let isPassed = String(data: data!, encoding:.utf8)?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
var gameViewControllerParams = [Int: [String: String]]()
gameViewControllerParams[0] = ["userId" : isPassed!]
gameViewControllerParams[1] = ["difficultyLvl" : difficultyLvl]
if(isPassed != "null"){
self.performSegue(withIdentifier: "gotoGame", sender: gameViewControllerParams)
}
}
task.resume()
}
GameViewController.swift
GameViewController.swift
class GameViewController: UIViewController {
var gameViewControllerParams = [Int: [String: String]]()
override func viewDidLoad() {
super.viewDidLoad()
let _ = Timer.scheduledTimer(timeInterval: 1.0, target:self, selector: #selector(self.setCalculationLs), userInfo:nil,repeats: true)
}
func setCalculationLs(){
print("Timing")
}
}
推荐答案
计时器在后台队列上不起作用(不费吹灰之力就涉及创建运行循环或在现有运行循环上手动进行调度).但是无论如何,您永远都不应从主队列之外的任何其他位置启动任何UI更新.
Timers don't work on background queues (without some sleight of hand involving creating run loops or manually scheduling it on an existing run loop). But you should never initiate any UI update from anything other than the main queue, anyway.
因此,由于您要从URLSession
完成闭包(在后台队列中运行)调用performSegue
,因此它实际上也在后台队列中运行viewDidLoad
.因此,计划计时器的尝试失败.要解决此问题,您必须手动将performSegue
代码分派到主队列:
So, since you're calling performSegue
from a URLSession
completion closure (which runs on a background queue), it's actually running viewDidLoad
from the background queue, too. Thus the attempt to schedule the timer is failing. To get around this, you have to manually dispatch the performSegue
code to the main queue:
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
...
if isPassed != "null" {
DispatchQueue.main.async {
self.performSegue(withIdentifier: "gotoGame", sender: ...)
}
}
}
如果您不确定是否在主队列上运行了某些代码,请参考文档.或者,您可以使用调度先决条件:
If you're ever unsure whether some code is running on the main queue or not, refer to the documentation. Or you can use a dispatch precondition:
dispatchPrecondition(condition: .onQueue(.main))
这样,如果您不小心从后台队列中调用了代码,它将(在调试版本中)停止应用程序.
That way it will (in debug builds) stop the app if you've accidentally invoked the code from a background queue.
与当前问题无关,但顺便说一句,为避免计时器和视图控制器之间的引用周期过长,通常希望保留对计时器的引用,以便在视图消失时可以invalidate
(例如,在viewDidAppear
中创建计时器,然后在viewDidDisappear
中将其删除).否则,您可以在GameViewController
被关闭后保留它,例如:
Unrelated to your current problem, but as an aside, to avoid a strong reference cycle between the timer and the view controller, you generally want to keep a reference to the timer so that you can invalidate
it when the view disappears (e.g. create timer in viewDidAppear
and remove it in viewDidDisappear
). Otherwise you can end up retaining the GameViewController
after it was dismissed, e.g.:
class GameViewController: UIViewController {
weak var timer: Timer?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
timer = Timer.scheduledTimer(timeInterval: 1.0, target:self, selector: #selector(setCalculationLs(_:)), userInfo: nil, repeats: true)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
timer?.invalidate()
}
@objc func setCalculationLs(_ timer: Timer) {
print("Tick")
}
}
或者在iOS 10或更高版本中,您可以将基于块的变体与对self
的weak
引用和deinit
中的invalidate
引用一起使用:
Or in iOS 10 or later, you can use the block-based variant with weak
reference to self
, and invalidate
in deinit
:
class GameViewController: UIViewController {
weak var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in
self?.setCalculationLs()
}
}
deinit {
timer?.invalidate()
}
func setCalculationLs() {
print("Tick")
}
}
这篇关于Swift Timer.scheduledTimer()不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!