Swift中Firebase循环加载多图失败问题求助
问题描述
需求:
- 从Firestore数据库加载每个itemcode对应的图片URL(部分itemcode的URL以数组形式存储)
- 加载每个itemcode对应的所有图片(按数组反向顺序)
- 将itemcode及其对应图片以列表形式展示
问题:调用mrPicCall函数时,仅单张图片的itemcode能正常加载,多图的itemcode加载失败。怀疑是计数器i、j的使用问题,但无法定位修复。
相关代码
func mrPicCall(MRCode: [MRCode]) { self.mrCodeArray = MRCode.map { $0.code } self.imageurls = [] self.selectedImages = [:] self.i = 0 self.j = 0 mrPicLoad() { (complete) in print(complete) self.booMRPicLoad = true } } func mrPicLoad(completion: @escaping (String) -> Void) { self.j = self.mrCodeArray.count for index in 0...(j - 1) { mrPicLoop(index: index) { (complete) in print(complete) if self.mrPicResponse.count == (self.j) { completion("5. MR list loaded!") } } } } func mrPicLoop(index: Int, completion: @escaping (String) -> Void) { let itemcode = mrCodeArray[index] loadPicDataFromStroage(itemcode: itemcode) { (complete) in print(complete) self.mrPicResponse.updateValue(MRPicResponse(selectedImages: self.selectedImages), forKey: index) completion("4. \(itemcode): Item \(index + 1) loaded.") } } func loadPicDataFromStroage(itemcode: String, completion: @escaping (String) -> Void) { // Get data from img database let db = Firestore.firestore() let docRef = db.collection("img_database").document(itemcode) self.imageurls = [] self.selectedImages = [:] docRef.getDocument { snapshot, error in if error == nil && snapshot != nil { self.imageurls = snapshot!["url"] as! [String] self.imageurls.reverse() self.loadMRImageFromURL(itemcode: itemcode) { (complete) in print(complete) completion("3. \(itemcode): All image data loaded!") } } else { self.errMRPic = true print(error as Any) } } } func loadMRImageFromURL(itemcode: String, completion: @escaping (String) -> Void) { self.i = imageurls.count for index in 0...(i - 1) { loopThroughImageLoad(index: index, itemcode: itemcode) { (complete) in print(complete) if self.selectedImages.count == (self.i) { completion("2. \(itemcode): All images loaded!") } } } } func loopThroughImageLoad(index: Int, itemcode: String, completion: @escaping (String) -> Void) { let value = imageurls[index] loadImageFromStorage(picurl: value) { (imageLoaded) in self.selectedImages.updateValue(imageLoaded, forKey: index) completion("1. \(itemcode): Image \(index + 1) loaded.") } } func loadImageFromStorage(picurl: String, completion: @escaping (UIImage) -> Void) { let storageRef = storage.reference() let fileRef = storageRef.child(picurl) fileRef.getData(maxSize: 5 * 1024 * 1024) { data, error in if error == nil && data != nil { if let image = UIImage(data: data!) { completion(image) } } else { self.errMRPic = true print(error as Any) } } }
问题根源与修复方案
核心问题
- 全局变量被并发请求覆盖:
imageurls、selectedImages、i、j均为全局变量,多个itemcode并行处理时,后一个请求会直接覆盖前一个请求的变量值,导致多图加载逻辑完全混乱。 - 完成判断逻辑不可靠:依赖
selectedImages.count == i判断图片加载完成,但并发场景下变量被覆盖后,计数判断完全失效。 - 数据存储逻辑错误:
mrPicResponse存储的是全局的selectedImages,而非当前itemcode对应的独立图片集合,最终导致所有itemcode的图片数据混淆。
修复步骤
1. 替换全局变量为局部变量/独立数据结构
取消全局的imageurls、selectedImages、i、j,改为在每个函数内使用局部变量,为每个itemcode维护独立的图片集合。
2. 重构loadPicDataFromStroage函数
func loadPicDataFromStroage(itemcode: String, completion: @escaping (String, [UIImage]?) -> Void) { let db = Firestore.firestore() let docRef = db.collection("img_database").document(itemcode) docRef.getDocument { snapshot, error in guard let snapshot = snapshot, error == nil else { self.errMRPic = true print(error as Any) completion("3. \(itemcode): Failed to load URL data", nil) return } guard let urls = snapshot["url"] as? [String] else { completion("3. \(itemcode): No URL data found", nil) return } let reversedUrls = urls.reversed() self.loadMRImages(from: reversedUrls, itemcode: itemcode) { images in completion("3. \(itemcode): All image data loaded!", images) } } }
3. 用DispatchGroup处理图片并发加载
替代计数器,用DispatchGroup精准追踪所有图片加载完成状态:
func loadMRImages(from urls: [String], itemcode: String, completion: @escaping ([UIImage]) -> Void) { var images: [UIImage] = [] let group = DispatchGroup() for url in urls { group.enter() loadImageFromStorage(picurl: url) { image in images.append(image) group.leave() } } group.notify(queue: .main) { completion(images) } } func loadImageFromStorage(picurl: String, completion: @escaping (UIImage) -> Void) { let storageRef = storage.reference() let fileRef = storageRef.child(picurl) fileRef.getData(maxSize: 5 * 1024 * 1024) { data, error in if let error = error { self.errMRPic = true print(error) // 加载失败返回占位图,避免阻塞流程 completion(UIImage()) return } guard let data = data, let image = UIImage(data: data) else { completion(UIImage()) return } completion(image) } }
4. 重构mrPicLoop和mrPicLoad,确保数据独立
func mrPicLoad(completion: @escaping (String) -> Void) { let group = DispatchGroup() self.mrPicResponse.removeAll() for (index, itemcode) in mrCodeArray.enumerated() { group.enter() mrPicLoop(index: index, itemcode: itemcode) { group.leave() } } group.notify(queue: .main) { completion("5. MR list loaded!") self.booMRPicLoad = true } } func mrPicLoop(index: Int, itemcode: String, completion: @escaping () -> Void) { loadPicDataFromStroage(itemcode: itemcode) { message, images in print(message) if let images = images { // 存储当前itemcode对应的独立图片集合 self.mrPicResponse[index] = MRPicResponse(selectedImages: images.enumerated().reduce(into: [:]) { $0[$1.offset] = $1.element }) } completion() } }
5. 简化mrPicCall函数
func mrPicCall(MRCode: [MRCode]) { self.mrCodeArray = MRCode.map { $0.code } self.mrPicResponse.removeAll() self.errMRPic = false mrPicLoad() { complete in print(complete) } }
关键改进点
- 用
DispatchGroup替代全局计数器,精准追踪并发任务完成状态 - 每个itemcode的图片数据独立存储,彻底避免全局变量覆盖问题
- 完善错误处理,加载失败时返回占位图,保证流程不中断
- 数据结构更清晰,确保
mrPicResponse中存储的是对应itemcode的正确图片集合
内容的提问来源于stack exchange,提问作者timothy13232
相关产品推荐
相关产品推荐

