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

Swift 5.0中Firestore数据转对象列表无法返回使用的问题

问题根源:异步操作的执行时机

Firestore的getDocuments是异步方法——它会在后台发起网络请求获取数据,只有当请求完成后,你传入的闭包才会被执行。而你在viewDidLoad里调用getUsers()后立刻执行print(ships.count),这时候闭包还没开始执行,ships数组自然是空的,所以输出0。闭包里的打印能正常输出,是因为那部分代码是在网络请求完成、数据解码后才运行的。


解决方案1:使用闭包回调(适配Swift 5.0)

修改getUsers方法,添加一个完成回调,当数据加载并处理完成后通知调用者,这样就能在数据准备好的时机安全使用ships数组。

修改后的代码示例:

class ViewController: UIViewController {
    lazy var usersCollection = Firestore.firestore().collection("ship")
    var ships: [MyShip] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        getUsers { [weak self] in
            guard let self = self else { return }
            // 这里闭包执行时,ships已经完成填充
            print(self.ships.count)
            // 在这里执行需要使用ships的逻辑,比如刷新UI
        }
    }
    
    func getUsers(completion: @escaping () -> Void) {
        usersCollection.getDocuments { (snapshot, error) in
            guard let snapshot = snapshot, error == nil else {
                // 处理错误情况
                print(error?.localizedDescription ?? "获取数据失败")
                completion()
                return
            }
            do {
                let myShip: [MyShip] = try snapshot.decoded()
                self.ships = myShip // 直接赋值替代循环append,更高效
                print(self.ships[1].nlloyds)
                completion() // 通知调用者数据已准备完成
            } catch {
                print("解码失败:\(error)")
                completion()
            }
        }
    }
}

关键细节:

  • 添加completion逃逸闭包参数:因为闭包会在异步操作完成后执行,需要标记为@escaping。
  • 替换强制解包和尝试:原来的try!和snapshot!会在出错时直接崩溃,改用do-catch和可选绑定处理错误,让代码更健壮。
  • 使用[weak self]避免内存泄漏:在闭包中捕获self时,用弱引用避免ViewController被意外持有导致内存泄漏。

解决方案2:升级到Swift 5.5+使用async/await(更优雅)

如果你的项目可以升级到Swift 5.5或更高版本,推荐使用async/await处理异步操作,代码会更简洁易读:

class ViewController: UIViewController {
    lazy var usersCollection = Firestore.firestore().collection("ship")
    var ships: [MyShip] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 用Task启动异步任务
        Task {
            await getUsers()
            print(ships.count)
            // 在这里执行需要使用ships的逻辑
        }
    }
    
    func getUsers() async {
        do {
            let snapshot = try await usersCollection.getDocuments()
            let myShip: [MyShip] = try snapshot.decoded()
            self.ships = myShip
            print(self.ships[1].nlloyds)
        } catch {
            print("获取或解码数据失败:\(error)")
        }
    }
}

关键细节:

  • 将getUsers标记为async方法,使用try await调用Firestore的异步API(需要iOS 15+支持)。
  • 在viewDidLoad中用Task包裹异步逻辑,等待getUsers执行完成后再处理ships数组。

内容的提问来源于stack exchange,提问作者Théo MARIE

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:20:22