Swift Timer.scheduledTimer() 不起作用 [英] Swift Timer.scheduledTimer() doesn't work
问题描述
我的 swift 应用中有两个视图.我正在执行如下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 或更高版本中,您可以使用基于块的变体,其中 weak
引用 self
和 invalidate
在 中>deinit
:
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屋!