Swift Timer.scheduledTimer() 不起作用 [英] Swift Timer.scheduledTimer() doesn't work

查看:39
本文介绍了Swift Timer.scheduledTimer() 不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的 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 引用 selfinvalidate 中>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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆