简体   繁体   English

Swift Timer.scheduledTimer() 不工作

[英]Swift Timer.scheduledTimer() doesn't work

I have two views in my swift app.我的 swift 应用程序中有两个视图。 I am performing a segue as below.我正在执行如下的 segue。

ViewController.swift -----------------> GameViewController.swift ViewController.swift ------------------> GameViewController.swift

When loading the GameViewController an value array also passed to GameViewController.swift from ViewController.swift加载 GameViewController 时,值数组也从 ViewController.swift 传递给 GameViewController.swift

A timer should be initialized in GameViewController.swift应在 GameViewController.swift 中初始化计时器

I tried to initialize a timer and call a method through it, but it doesn't work.我试图初始化一个计时器并通过它调用一个方法,但它不起作用。

Followings are my code snippets.以下是我的代码片段。

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")
    }

}

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. 但是你绝不应该从主队列以外的任何东西发起任何UI更新。

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. 因此,由于您从URLSession完成闭包(在后台队列上运行)调用performSegue ,它实际上也在后台队列中运行viewDidLoad 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: 要解决此问题,您必须手动将performSegue代码分派到主队列:

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. 这样,如果您不小心从后台队列中调用了代码,它将(在调试版本中)停止应用程序。


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 (eg create timer in viewDidAppear and remove it in viewDidDisappear ). 与您当前的问题无关,但另外,为了避免计时器和视图控制器之间的强引用周期,您通常希望保留对计时器的引用,以便在视图消失时invalidate (例如,创建计时器) viewDidAppear并在viewDidDisappear删除它。 Otherwise you can end up retaining the GameViewController after it was dismissed, eg: 否则你可以在解雇后最终保留GameViewController ,例如:

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")
    }
}

Or in iOS 10 or later, you can use the block-based variant with weak reference to self , and invalidate in deinit : 或者在iOS 10或更高版本中,您可以使用基于块的变体,对self进行weak引用,并在deinit invalidate

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 5.5 Swift 5.5

I was having this issue as well while working in a segued view controller. Since segues are executed as a background thread to the main thread (main view controller), running timer within the segued view controller won't work.我在 segued 视图 controller 中工作时也遇到了这个问题。由于 segues 是作为主线程(主视图控制器)的后台线程执行的,因此在 segued 视图 controller 中运行计时器将不起作用。 So, that being known and also knowing timers don't work in background threads, I did some digging and found this Apple Documentation gem.因此,众所周知并且知道计时器在后台线程中不起作用,我进行了一些挖掘并找到了这个 Apple Documentation gem。

I added the following line right after my scheduled timer.我在预定的计时器之后添加了以下行。 The timer now works beautifully.计时器现在工作得很好。 It is just sending the timer to the main thread for execution.它只是将定时器发送到主线程执行。

myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.updateWithTimer), userInfo: nil, repeats: true)
RunLoop.main.add(myTimer, forMode: .common)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM