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

使用URLSession时跨类访问products数组为空的问题排查与解决

问题分析与解决方案

嘿,我帮你拆解下这个问题——你遇到的其实是异步网络请求的时序问题,和do/catch代码块完全没关系!

为啥其他类访问products时总是空的?

你用的URLSession.shared.dataTask(with:)是个异步操作:它会在后台线程默默跑网络请求、解析数据,而调用retrieveProductList()的主线程(或者其他线程)不会等它做完,直接就继续执行后续代码了。

举个实际的执行顺序:

  1. 你在其他类调用retrieveProductList()
  2. 代码立刻跑去访问products数组,但这时候后台的网络请求还没返回,数组自然是空的
  3. 等网络请求完成、数据解析完,闭包里的print才会输出有内容的数组——但这时候你早就已经尝试访问过空数组了

怎么让其他类能拿到填充好的products?

核心思路是:等数据准备好之后,主动通知调用方,而不是让调用方直接去读products属性。这里给你两种最常用的实现方式:

方案1:用回调闭包(兼容所有Swift版本)

修改你的retrieveProductList方法,加一个完成回调,当数据解析完就把结果传出去:

private let productListUrl = URL(string: "https://api/products.json")
var products = [Product]()

func retrieveProductList(completion: @escaping ([Product]?, Error?) -> Void) {
    guard let productListUrl = productListUrl else {
        completion(nil, NSError(domain: "Invalid URL", code: -1, userInfo: nil))
        return
    }
    
    URLSession.shared.dataTask(with: productListUrl) { (data, response, error) in
        // 先处理网络错误
        if let error = error {
            completion(nil, error)
            return
        }
        
        guard let data = data else {
            completion(nil, NSError(domain: "No data received", code: -2, userInfo: nil))
            return
        }
        
        do {
            let jsonData = try JSONSerialization.jsonObject(with: data, options: []) as! [String:Any]
            // 这里别用as!,改成guard let更安全,防止JSON结构不对崩溃
            guard let tempArray = jsonData["products"] as? [[String:Any]] else {
                completion(nil, NSError(domain: "Invalid JSON structure", code: -3, userInfo: nil))
                return
            }
            
            // 用compactMap替代循环,更简洁,还能自动过滤nil的Product
            self.products = tempArray.compactMap { Product(json: $0) }
            print("In ProductService: \(self.products)")
            // 通知调用方数据准备好了
            completion(self.products, nil)
        } catch {
            print("Error parsing data: \(error)")
            completion(nil, error)
        }
    }.resume()
}

然后在其他类里这么调用:

let service = ProductService()
service.retrieveProductList { products, error in
    if let error = error {
        print("Oops, fetch failed: \(error)")
        return
    }
    
    guard let products = products else {
        print("No products found")
        return
    }
    
    // 这里的products就是填充好的!放心用
    print("From other class: \(products)")
    // 如果要更新UI,记得切回主线程
    DispatchQueue.main.async {
        // 比如刷新你的表格/列表视图
    }
}

方案2:用Async/Await(Swift 5.5+,更清爽)

如果你的项目用的是Swift 5.5及以上版本,强烈推荐用异步等待语法,代码可读性高很多:

private let productListUrl = URL(string: "https://api/products.json")
var products = [Product]()

func retrieveProductList() async throws -> [Product] {
    guard let productListUrl = productListUrl else {
        throw NSError(domain: "Invalid URL", code: -1, userInfo: nil)
    }
    
    // 用await等待网络请求完成
    let (data, _) = try await URLSession.shared.data(from: productListUrl)
    let jsonData = try JSONSerialization.jsonObject(with: data, options: []) as! [String:Any]
    
    guard let tempArray = jsonData["products"] as? [[String:Any]] else {
        throw NSError(domain: "Invalid JSON structure", code: -3, userInfo: nil)
    }
    
    self.products = tempArray.compactMap { Product(json: $0) }
    print("In ProductService: \(self.products)")
    return self.products
}

调用的时候要放在异步上下文里(比如Task):

let service = ProductService()
Task {
    do {
        let products = try await service.retrieveProductList()
        // 这里拿到的就是完整的products数组
        print("From other class: \(products)")
        // 更新UI要切回主线程,用MainActor.run
        await MainActor.run {
            // 刷新UI操作
        }
    } catch {
        print("Fetch error: \(error)")
    }
}

额外小建议

  • 尽量别用as!强制解包,用guard let或者if let,不然JSON结构一变你的APP就崩溃了
  • 如果你的Product结构体还没实现Codable,建议加上!用JSONDecoder解析比JSONSerialization安全又简洁,比如:
    let products = try JSONDecoder().decode([Product].self, from: data)
    
  • 所有UI操作必须在主线程执行,所以拿到数据后要记得切线程哦!

内容的提问来源于stack exchange,提问作者Tyklonix

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:56:47