You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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-

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 20:35:02