SwiftUI中使用Resolver导致崩溃,相关组件调用顺序是怎样的?
问题根源
- 循环依赖死锁:AuthSession初始化时直接解析OfferRepository实例,而OfferRepository初始化时又需要注入AuthSession,两个应用级单例的初始化过程形成死循环,导致内存访问错误。
- 属性包装器误用:
@InjectedObject是Resolver专为SwiftUI视图设计的属性包装器,在非View的ObservableObject服务类中使用需要用@Injected,前者会在初始化阶段强制立即获取实例,进一步加剧循环依赖问题。 - Firebase实例未完成初始化:循环初始化过程中提前触发了Firestore实例的访问,此时Firebase相关配置还未完全加载,因此在Firestore内部方法中抛出EXC_BAD_ACCESS崩溃。
解决方案
- 修正属性包装器使用:将OfferRepository中注入AuthSession的属性包装器从
@InjectedObject改为@Injected,降低实例解析的优先级。 - 延迟解析仓库实例:将AuthSession中的OfferRepository属性改为懒加载,仅在第一次使用时才解析实例,避免初始化阶段就触发循环。
- 统一管控监听器生命周期:移除OfferRepository初始化方法中的监听器启动逻辑,改为由AuthSession在登录/登出状态回调中主动调用仓库的监听器启停方法,完全符合你希望将鉴权相关逻辑收口到AuthSession的需求。
修正后代码示例
AuthSession.swift
class AuthSession: ObservableObject { let db = Firestore.firestore() // 改为懒加载,避免初始化时立刻解析OfferRepository lazy var offerRepository: OfferRepository = Resolver.resolve() var handle: AuthStateDidChangeListenerHandle? @Published var currentUser: User? @Published var loggedIn = false @Published var currentUserUid = "" private var cancellables = Set<AnyCancellable>() init() {} func listen() { print("AuthSession - listen called") handle = Auth.auth().addStateDidChangeListener{ (auth, user) in if let user = user { print("User Exists.") self.loggedIn = true self.currentUserUid = user.uid self.currentUser = user // 登录成功后启动仓库监听器 self.offerRepository.startSnapshotListener() } else { print("Not logged in") self.loggedIn = false self.currentUserUid = "" self.currentUser = nil // 登出时停止监听器、清空数据 self.offerRepository.stopSnapshotListener() } } } }
OfferRepository.swift
class OfferRepository: ObservableObject { let db = Firestore.firestore() private var snapshotListener: ListenerRegistration? // 替换为适用于服务类的@Injected @Injected var authSession: AuthSession @Published var offers = [Offer]() private var cancellables = Set<AnyCancellable>() init() { // 移除初始化阶段的监听器启动逻辑,统一交给AuthSession管控 } // 新增停止监听器的方法 func stopSnapshotListener() { snapshotListener?.remove() snapshotListener = nil offers.removeAll() } func startSnapshotListener() { guard snapshotListener == nil, !authSession.currentUserUid.isEmpty else { return } self.snapshotListener = db.collection(FirestoreCollection.offers).order(by: "created", descending: true).addSnapshotListener { (querySnapshot, error) in if let error = error { print("Error getting documents: \(error)") } else { guard let documents = querySnapshot?.documents else { print("No Offers.") return } self.offers = documents.compactMap { offer in do { return try offer.data(as: Offer.self) } catch { print(error) } return nil } } } } }
Resolver注册代码(无需修改)
extension Resolver: ResolverRegistering { public static func registerAllServices() { register { AuthSession() }.scope(.application) register { OfferRepository() as OfferRepository }.scope(.application) } }
内容的提问来源于stack exchange,提问作者jonthornham
相关产品推荐
相关产品推荐

