我正试图使用此函数修改全局变量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()
}
假设
currentWeather
是CurrentWeather
类中的静态变量,则可以更新全局变量并将实际数据返回给调用方,如上图所示编辑:
正如下面注释中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/