Swift API异步请求后外部变量无法保存数据问题求助
API异步请求后外部无法获取数据的问题
问题场景
调用heroesNames()方法后,全局变量heroNames始终为空,但在getInformation的闭包内部添加打印语句,能看到数据已经被正常添加到heroNames中:
before append[] after append["TZY"] before append["TZY"] after append["TZY", "YrikGood"] before append["TZY", "YrikGood"] after append["TZY", "YrikGood", "Newsham"]
相关代码
数据模型
// MARK: - ProPlayerElement struct ProPlayerElement: Codable { let name, avatarfull, personaname: String? let fantasyRole: Int? let teamName: String? enum CodingKeys: String, CodingKey { case name, avatarfull, personaname case fantasyRole = "fantasy_role" case teamName = "team_name" } } typealias ProPlayers = [ProPlayerElement]
API管理类
class ApiManager { static let shared = ApiManager() func getInformation(completion: @escaping (ProPlayers) -> Void) { let request = ApiType.proPlayers.request let task = URLSession.shared.dataTask(with: request) { data, response, error in if let data = data, let players = try? JSONDecoder().decode(ProPlayers.self, from: data) { completion(players) } else { completion([]) } } task.resume() } }
数据获取方法
var heroNames = [String] () func heroesNames () { ApiManager.shared.getInformation { players in for i in 0..<players.count-1 { self.heroNames.append(players[i].name!) } } }
问题原因
URLSession.dataTask是异步执行的,闭包内的代码会在后台线程运行,而你在闭包外部访问heroNames时,闭包的代码还没执行完毕,所以看不到更新后的数据。另外原代码中for i in 0..<players.count-1会漏掉最后一个玩家数据,属于逻辑小错误。
解决办法
方法1:给heroesNames添加完成闭包
在数据处理完成后,通过闭包通知外部获取结果,同时避免强制解包:
func heroesNames(completion: @escaping ([String]) -> Void) { ApiManager.shared.getInformation { players in // 用compactMap过滤掉name为nil的情况,替代强制解包 let validNames = players.compactMap { $0.name } self.heroNames = validNames // 通知外部数据已准备好 completion(validNames) } }
调用方式:
heroesNames { fetchedNames in // 在这里处理获取到的数据,比如更新UI、打印等 print(fetchedNames) }
方法2:结合响应式更新(SwiftUI场景)
如果是SwiftUI项目,可将heroNames放在ObservableObject中,用@Published自动触发UI更新:
class PlayerViewModel: ObservableObject { @Published var heroNames = [String]() func fetchHeroes() { ApiManager.shared.getInformation { players in // UI更新必须在主线程执行 DispatchQueue.main.async { self.heroNames = players.compactMap { $0.name } } } } }
额外优化点
- 永远不要用
!强制解包可选值,一旦API返回name为nil,会直接导致崩溃,用compactMap或if let安全处理。 - 遍历数组时直接用
for player in players替代下标遍历,代码更简洁易读。
内容的提问来源于stack exchange,提问作者Resurrect1on-
相关产品推荐
相关产品推荐

