下载Firebase Storage数据时DispatchGroup执行异常,photoImage始终为nil怎么解决?
问题排查及修复方案
核心问题点
- 下载图片的错误分支未调用
group.leave():当图片下载出错时直接return,没有触发离开组的逻辑,导致group.wait()无限等待,后续赋值和登录逻辑永远不会执行 DispatchGroup定义为全局实例属性:如果多次调用fetchUserData会导致组计数混乱,应该改为方法内的局部变量- 下载成功后未保证在主队列赋值UI相关属性:
UIImage赋值属于UI相关操作,Firebase Storage的回调默认不是主队列,直接赋值可能引发异常甚至赋值失败 photoImage未标记为@Published:如果需要在SwiftUI视图中监听这个属性的变化,没有@Published修饰的话视图不会收到更新通知,也会出现看起来赋值失败的假象
修复后的代码
class AppViewModel: ObservableObject { private var db = Firestore.firestore() @Published var userInfo: User? @Published var signedIn: Bool = false @Published var photoImage: UIImage? // 新增@Published修饰 var handle: AuthStateDidChangeListenerHandle? let authRef = Auth.auth() var authHandle : AuthStateDidChangeListenerHandle? var rootInfoCollection : CollectionReference! var userIdRef = "" var downloadImageTask: StorageReference? // 移除全局的DispatchGroup,改为方法内局部变量 func fetchUserData(){ db.collection("Users").document("\(userIdRef)").getDocument { [self] document, error in // 合并空值判断逻辑简化代码 if error == nil, let document = document, document.exists { self.userInfo = document.map { (documentSnapshot) -> User in let data = documentSnapshot.data() let uid = data?["uid"] as? UUID ?? UUID() let company = data?["company"] as? String ?? "" let name = data?["name"] as? String ?? "" let admin = data?["admin"] as? Bool ?? false let photoRef = data?["photoRef"] as? String ?? "" self.downloadImageTask = Storage.storage().reference().child(photoRef) return User(uid: uid, company: company, name: name, admin: admin, photoRef: photoRef, photoImage: nil) } let group = DispatchGroup() // 局部DispatchGroup,避免多次调用冲突 guard let downloadTask = downloadImageTask else { // 没有下载任务直接走登录逻辑 DispatchQueue.main.async { withAnimation { self.signedIn = true } } return } group.enter() downloadTask.getData(maxSize: 6 * 1024 * 1024) { data, error in defer { group.leave() } // 用defer保证不管是否出错都调用leave if let error = error { print("Got an error fetching data: \(error.localizedDescription)") return } guard let data = data, let image = UIImage(data: data) else { print("图片数据无效") return } DispatchQueue.main.async { // 主队列赋值UI属性保证生效 self.photoImage = image } } group.notify(queue: .main) { // 用notify替代wait,不会阻塞线程 if self.photoImage != nil { print("Photo Image Is Set") } withAnimation { self.signedIn = true } } } } } }
额外优化说明
- 用
group.notify替代group.wait:wait会阻塞当前线程,notify是任务全部完成后自动回调到指定队列,性能更好也不会有死锁风险 - 新增空值判断避免强制解包 crash:去掉了
data!的强制解包,增加有效性校验 - 用
defer统一管理group.leave:避免漏写离开逻辑的问题
内容的提问来源于stack exchange,提问作者RapsIn4
相关产品推荐
相关产品推荐

