我正试图使用此函数修改全局变量currentwather(currentwather类型),这意味着用从URL检索到的信息更新所述变量,并返回一个bool,表示它成功了。但是,函数返回false,因为currentWeather仍然为nil。我认识到dataTask是异步的,并且任务是在与应用程序并行的后台运行的,但是我不明白这对于我要完成的任务意味着什么。我也不能更新Dead块之后的CurrnWeeWek,因为退出块后不再识别天气。我确实尝试过使用“self.currentWeather”,但被告知它是一个未解析的标识符(可能是因为该函数也是全局的,并且没有“self”?).
URL当前无效,因为我取出了API密钥,但它按预期工作,否则,我的CurrentWeather结构是可解码的。打印currentWeatherUnwrapped也始终成功。
我确实查看了一下Stack Overflow和Apple的官方文档,但找不到能够回答我问题的东西,但也许我还不够彻底。如果这是个重复的问题,我很抱歉。如有进一步相关阅读,敬请指点!我很抱歉没有遵守最佳编码实践-我在这一点上没有太多经验。非常感谢你们!

func getCurrentWeather () -> Bool {
let jsonUrlString = "https://api.wunderground.com/api/KEY/conditions/q/\(state)/\(city).json"

guard let url = URL(string: jsonUrlString) else { return false }

URLSession.shared.dataTask(with: url) { (data, response, err) in
    // check error/response

    guard let data = data else { return }

    do {
        let weather = try JSONDecoder().decode(CurrentWeather.self, from: data)
        currentWeather = weather
        if let currentWeatherUnwrapped = currentWeather {
            print(currentWeatherUnwrapped)
        }
    } catch let jsonErr {
        print("Error serializing JSON: ", jsonErr)
    }

    // cannot update currentWeather here, as weather is local to do block

    }.resume()

return currentWeather != nil
}

最佳答案

你只需要一个了结。
不能使用同步返回语句来返回web服务调用的响应,而web服务调用本身就是异步的。你需要关闭它。
你可以修改你的答案如下。因为你在评论中没有回答我的问题,所以我冒昧地退回了湿的东西,而不是退回没有多大意义的布尔。

func getCurrentWeather (completion : @escaping((CurrentWeather?) -> ()) ){
        let jsonUrlString = "https://api.wunderground.com/api/KEY/conditions/q/"

        guard let url = URL(string: jsonUrlString) else { return false }

        URLSession.shared.dataTask(with: url) { (data, response, err) in
            // check error/response

            guard let data = data else { return }

            do {
                let weather = try JSONDecoder().decode(CurrentWeather.self, from: data)
                CurrentWeather.currentWeather = weather
                if let currentWeatherUnwrapped = currentWeather {
                    completion(CurrentWeather.currentWeather)
                }
            } catch let jsonErr {
                print("Error serializing JSON: ", jsonErr)
                completion(nil)
            }

            // cannot update currentWeather here, as weather is local to do block

            }.resume()
    }

假设currentWeatherCurrentWeather类中的静态变量,则可以更新全局变量并将实际数据返回给调用方,如上图所示
编辑:
正如下面注释中Duncan指出的,上面的代码在后台线程中执行完成块。所有的UI操作只能在主线程上完成。因此,在更新UI之前切换线程是非常重要的。
两种方式:
1-确保在主线程上执行完成块。
DispatchQueue.main.async {
      completion(CurrentWeather.currentWeather)
}

这将确保将来使用getCurrentWeather的人不必担心切换线程,因为您的方法会处理它。如果您的完成块只包含要更新UI的代码,则会很有用。使用这种方法的完成块中的较长逻辑将负担主线程。
2-Else在completion块中,每当更新UI元素时,都将其作为参数传递给getCurrentWeather,确保将这些语句包装在
DispatchQueue.main.async {
    //your code to update UI
}

编辑2:
正如Leo Dabus在下面的注释中指出的,我应该运行completion block而不是guard let url = URL(string: jsonUrlString) else { return false }这是一个复制粘贴错误。我抄下了操作人员的问题,匆忙中发现有一个返回语句。
虽然在这种情况下,将错误作为参数是可选的,并且完全取决于您设计错误处理模型的方式,但我很欣赏Leo Dabus建议的想法,这是一种更为通用的方法,因此我会将我的答案更新为将错误作为参数。
现在有些情况下,我们可能还需要发送自定义错误,例如如果guard let data = data else { return }返回false而不是简单地调用return,则可能需要返回一个您自己的错误,该错误表示无效输入或类似的内容。
因此,我可以自行声明一个自定义错误,您也可以使用该模型来处理您的错误处理
enum CustomError : Error {
    case invalidServerResponse
    case invalidURL
}

func getCurrentWeather (completion : @escaping((CurrentWeather?,Error?) -> ()) ){
        let jsonUrlString = "https://api.wunderground.com/api/KEY/conditions/q/"

        guard let url = URL(string: jsonUrlString) else {
            DispatchQueue.main.async {
                completion(nil,CustomError.invalidURL)
            }
            return
        }

        URLSession.shared.dataTask(with: url) { (data, response, err) in
            // check error/response

            if err != nil {
                DispatchQueue.main.async {
                    completion(nil,err)
                }
                return
            }

            guard let data = data else {
                DispatchQueue.main.async {
                    completion(nil,CustomError.invalidServerResponse)
                }
                return
            }

            do {
                let weather = try JSONDecoder().decode(CurrentWeather.self, from: data)
                CurrentWeather.currentWeather = weather

                if let currentWeatherUnwrapped = currentWeather {
                    DispatchQueue.main.async {
                        completion(CurrentWeather.currentWeather,nil)
                    }
                }

            } catch let jsonErr {
                print("Error serializing JSON: ", jsonErr)
                DispatchQueue.main.async {
                    completion(nil,jsonErr)
                }
            }

            // cannot update currentWeather here, as weather is local to do block

            }.resume()
    }

关于ios - 修改全局变量内部闭包(Swift 4),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47272694/

10-12 12:29