ViewDidLoad中Firebase异步操作执行顺序问题及方案咨询
嘿,这个问题我太熟了——本质就是异步操作的典型陷阱!先给你把执行顺序颠倒的原因讲明白,再给你最优方案和实现代码~
为什么代码书写顺序正确但执行顺序颠倒?
Firebase 的 observe(.childAdded) 是异步网络操作:当你调用这个方法时,系统会把网络请求放到后台线程执行,主线程不会停下来等请求完成,而是直接继续往下跑 createAlbums() 和 setupConstraints()。等数据库请求拿到数据后,才会触发闭包里的代码——这就是为什么你看到//1的代码反而晚于//2执行的核心原因。
最优方案及实现方法
根据你的需求(是一次性加载现有专辑,还是需要实时监听新增专辑),推荐两种不同的最优方案:
方案1:一次性加载现有专辑(推荐大多数场景)
如果你的需求只是加载当前数据库里的所有专辑,不需要实时监听后续新增,用 observeSingleEvent(of: .value) 替代 childAdded,再配合完成回调来确保数据加载完成后再执行依赖代码。
实现代码:
import FirebaseDatabase class YourViewController: UIViewController { var albums: [String] = [] private let dbRef = Database.database().reference() // 封装带完成回调的专辑获取方法 private func fetchAllAlbums(completion: @escaping ([String]) -> Void) { dbRef.child("Albums").observeSingleEvent(of: .value) { snapshot in var fetchedAlbums = [String]() // 遍历所有子节点获取key for childSnapshot in snapshot.children.allObjects as! [DataSnapshot] { fetchedAlbums.append(childSnapshot.key) } // 数据准备好后触发回调 completion(fetchedAlbums) } } override func viewDidLoad() { super.viewDidLoad() // 调用获取方法,在回调里执行依赖代码 fetchAllAlbums { [weak self] fetchedAlbums in guard let self = self else { return } self.albums = fetchedAlbums print("加载完成的专辑数组:\(self.albums)") // 现在可以安全执行依赖albums的代码 self.createAlbums() self.setupConstraints() } } // 你的createAlbums和setupConstraints方法 private func createAlbums() { /* ... */ } private func setupConstraints() { /* ... */ } }
方案2:实时监听专辑(包括初始加载+后续新增)
如果需要实时监听数据库里新增的专辑,那还是要用 observe(.childAdded),但需要加一个计数判断,确保所有初始专辑加载完成后再执行 createAlbums() 和 setupConstraints()。
实现代码:
import FirebaseDatabase class YourViewController: UIViewController { var albums: [String] = [] private let dbRef = Database.database().reference() override func viewDidLoad() { super.viewDidLoad() let albumsRef = dbRef.child("Albums") // 先获取专辑总数量,用来判断初始加载是否完成 albumsRef.observeSingleEvent(of: .value) { [weak self] snapshot in guard let self = self else { return } let totalInitialAlbums = snapshot.childrenCount var loadedCount = 0 // 开始监听childAdded albumsRef.observe(.childAdded) { snapshot in self.albums.append(snapshot.key) loadedCount += 1 print("当前专辑数组:\(self.albums)") // 当所有初始专辑加载完成后,执行依赖代码 if loadedCount == totalInitialAlbums { self.createAlbums() self.setupConstraints() } // 后续新增的专辑会继续触发这个闭包,你可以在这里更新UI(比如新增专辑卡片) } } } private func createAlbums() { /* ... */ } private func setupConstraints() { /* ... */ } }
对比你提到的其他方案
- 拆分到viewDidLoad和viewDidAppear:不推荐,因为
viewDidAppear会在页面每次显示时触发(比如从其他页面返回),容易重复执行代码,而且用户会先看到空白页面再加载内容,体验不好。 - 计数变量:就是方案2的思路,适合实时监听的场景,但如果只是一次性加载,方案1的回调更简洁。
内容的提问来源于stack exchange,提问作者Nicolò Padovan
相关产品推荐
相关产品推荐

