点击UIButton时Swift的Task与异步API执行顺序及返回nil问题排查
问题根源
你的代码存在异步操作的竞态问题:
performRequest方法中,URLSession.dataTask(with:completionHandler:)是异步执行的网络请求,调用task.resume()后,方法会立刻往下执行并返回初始值nil,不会等待网络请求完成。- 网络请求完成后,才会进入completion闭包解析数据并赋值给
weatherResult,但此时fetchWeather已经把nil返回给调用者了,所以会先打印weatherData: nil,之后才打印解析完成的decodedData。
另外,你在ViewController里用了Task和await,但fetchWeather并不是异步函数(没有async关键字),所以await在这里完全不起作用,无法等待网络请求完成。
修复方案
把WeatherManager的方法改成异步函数,利用Swift的async/await替代传统的completion handler,确保调用者能等待网络请求完成后拿到结果:
修改后的WeatherManager代码
struct WeatherManager{ let weatherURL = "https://api.openweathermap.org/data/2.5/weather?appid=someAPIKeyHere" // 改成异步函数,返回WeatherModel? func fetchWeather(cityName: String) async -> WeatherModel? { let urlString = "\(weatherURL)&q=\(cityName)" return await performRequest(urlString: urlString) } // 异步函数,用async/await处理网络请求 func performRequest(urlString: String) async -> WeatherModel? { guard let url = URL(string: urlString) else { return nil } do { // 用URLSession的异步方法替代completion handler let (data, _) = try await URLSession.shared.data(from: url) return parseJSON(weatherData: data) } catch { print("网络请求错误: \(error.localizedDescription)") return nil } } func parseJSON(weatherData: Data) -> WeatherModel?{ let decoder = JSONDecoder() do { let decodedData = try decoder.decode(WeatherResponse.self, from: weatherData) print("this is in decodedData: \(decodedData)") let temp = decodedData.main.temp let name = decodedData.name let weather = WeatherModel(conditionId:300, cityName: name, temperature: temp) return weather } catch { print("解析错误: \(error.localizedDescription)") return nil } } }
修改后的ViewController代码
@IBAction func callAPIButton(_ sender: UIButton) { Task { // 现在await会等待fetchWeather异步执行完成 let weatherData = await weatherManager.fetchWeather(cityName: "Seattle") print("This is in weatherData: \(weatherData ?? WeatherModel(conditionId: 0, cityName: "", temperature: 0.0))") } }
修复后效果
执行后会先等待网络请求和解析完成,再打印weatherData的内容,输出顺序和你期望的一致:
this is in decodedData: WeatherResponse(name: "Seattle", weather: [Awesome_Weather_App.WeatherAPI(description: "overcast clouds", icon: "04d")], main: Awesome_Weather_App.MainAPI(temp: 287.81, pressure: 1018.0, humidity: 44.0, temp_min: 284.91, temp_max: 290.42, feels_like: 286.48), sys: Awesome_Weather_App.SysAPI(sunrise: 1.6712886e+09, sunset: 1.6713243e+09)) This is in weatherData: WeatherModel(conditionId: 300, cityName: "Seattle", temperature: 287.81)
内容的提问来源于stack exchange,提问作者SushiRiceOverEggs
相关产品推荐
相关产品推荐

