繁体   English   中英

由于程序未按顺序执行的Swift方法不等待URLSession.shared.dataTask完成

[英]Swift Methods Executing Out of Order as Program is Not Waiting for URLSession.shared.dataTask to Finish

我正在尝试编写一个应用程序,其中从API检索数据并随后显示在应用程序视图中。 问题是,当我运行代码时,即使在getJson()函数之后调用它,在其中获取视图的setupViews()始终在检索JSON数据之前运行。 getJson()函数先运行,但在“ URLSession.shared.dataTask(with:the_urlObj){(data,_,error)in”处停止,然后跳过该函数的其余部分,然后执行所有其余的在程序返回之前先运行。 在调用setupViews()函数之前,如何获取完整的getJson()函数来执行?

我注意到,URLSession.shared.dataTask行是getJson()函数中的代码停止的位置。 在setupViews()函数已经执行之后,它将返回到该代码的其余部分。 我尝试在URLSession.shared.dataTask行之后的getJson()块中调用self.setupViews(),但是视图仍然显示而没有数据。

// viewDidLoad函数

override func viewDidLoad() {
    super.viewDidLoad()
    getJson()
    setupViews()
}

// getJson函数

func getJson(){
    let jsonString = "https://api.openweathermap.org/data/2.5/weather?q=brooklyn,us&APPID=f942c97cab0e663a9a4882e6c3f0db1e"
    guard let url = URL(string: jsonString) else { return }
    URLSession.shared.dataTask(with: url) { (data,response,err) in
        guard let data = data else { return }
    do {

        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        decoder.dateDecodingStrategy = .secondsSince1970
        let weatherData = try decoder.decode(WeatherData.self, from: data)
        print("temperature: ", weatherData.main.temp)
        // self.todays_weather.text = String("\(temperature)")
        temperature = Int(weatherData.main.temp)
        temperature = (temperature * 9/5)-459

    }
    catch {
        print(err)
        print(response)
    }

}.resume()

}

//设置视图功能

func setupViews(){
    self.view.addSubview(todays_weather)
    todays_weather.heightAnchor.constraint(equalToConstant: 140).isActive = true
    todays_weather.widthAnchor.constraint(equalToConstant: 150).isActive = true
    todays_weather.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
    todays_weather.centerYAnchor.constraint(equalTo: self.view.topAnchor, constant: 120).isActive = true

    self.view.addSubview(weather_button)
    weather_button.heightAnchor.constraint(equalToConstant: 240).isActive = true
    weather_button.widthAnchor.constraint(equalToConstant: 300).isActive = true
    weather_button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
    weather_button.centerYAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -90).isActive = true

}

let todays_weather : UITextField = {
    let weather = UITextField()
    weather.textColor = UIColor.black
    weather.textAlignment = .center
    weather.text = String("\(temperature)")
    weather.font = UIFont.systemFont(ofSize:32)
    weather.translatesAutoresizingMaskIntoConstraints = false
    return weather
}()

代码运行良好,但是视图中的文本字段显示为0而不是JSON检索的温度,因为由于getJson()函数在URLSession.shared处停止,因此setupViews()在JSON数据检索完成之前就已执行。 dataTask。

我在控制台中收到此通知:2019-07-29 11:44:01.060026-0400 daily_tips [46459:8017625]从主线程访问引擎后,此应用程序正在从后台线程修改AutoLayout引擎。 这会导致引擎损坏和奇怪的崩溃。

任何帮助,将不胜感激,谢谢!

欢迎来到StackOverflow! 对您来说,JSON提取是异步发生的,但是您在调用setupViews()之后立即调用getJson()

一个好的解决方案是在getJson()调用中添加一个完成处理程序。

func getJson(completionHandler: () -> ()){
    let jsonString = "https://api.openweathermap.org/data/2.5/weather?q=brooklyn,us&APPID=f942c97cab0e663a9a4882e6c3f0db1e"
    guard let url = URL(string: jsonString) else { return }
    URLSession.shared.dataTask(with: url) { (data,response,err) in
        guard let data = data else { return }
        do {

            let decoder = JSONDecoder()
            decoder.keyDecodingStrategy = .convertFromSnakeCase
            decoder.dateDecodingStrategy = .secondsSince1970
            let weatherData = try decoder.decode(WeatherData.self, from: data)
            print("temperature: ", weatherData.main.temp)
            // self.todays_weather.text = String("\(temperature)")
            temperature = Int(weatherData.main.temp)
            temperature = (temperature * 9/5)-459
            completionHandler()
        }
        catch {
            print(err)
            print(response)
        }

    }.resume()
}

然后在completionHandler中调用setupViews():

override func viewDidLoad() {
    super.viewDidLoad()
    getJson { [weak self] in
        DispatchQueue.main.async {
            self?.setupViews()
        }
    }
} 

我建议搜索“ ios异步编程”和“ ios完成处理程序”以了解更多信息。 有很多很棒的信息,希望对您有所帮助。

没有完成处理简单的解决方案,把setupViews()在完成处理dataTask

在openweathermap URL中使用参数units=imperial ,您将获得所有华氏度。

override func viewDidLoad() {
    super.viewDidLoad()
    getJson()
}

func getJson(){
    let jsonString = "https://api.openweathermap.org/data/2.5/weather?q=brooklyn,us&units=imperial&APPID=f942c97cab0e663a9a4882e6c3f0db1e"
    guard let url = URL(string: jsonString) else { return }
    URLSession.shared.dataTask(with: url) { [unowned self] data, response, error in
        guard let data = data else { print(error!); return }
    do {

        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        decoder.dateDecodingStrategy = .secondsSince1970
        let weatherData = try decoder.decode(WeatherData.self, from: data)
        print("temperature: ", weatherData.main.temp)
        // self.todays_weather.text = String("\(temperature)")
        temperature = Int(weatherData.main.temp)
        DispatchQueue.main.async {
            self.setupViews()
        }

    }
    catch {
        print(error)
        print(response)
    }

}.resume()

暂无
暂无

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

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