SwiftUI:在运行时访问 EnvironmentObject 访问时出现线程错误

问题描述

我有这几个环境对象的场景:

func scene(_ scene: UIScene,willConnectTo session: UIScenesession,options connectionoptions: UIScene.Connectionoptions) {
    // Get the managed object context from the shared persistent container.
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    // Create the SwiftUI view and set the context as the value for the managedobjectContext environment keyPath.
    // Add `@Environment(\.managedobjectContext)` in the views that will need the context.
    let contentView = MainTabView()
        //Inject Database repository
        .environmentObject(DatabaseRepository())
        .environmentObject(UserRepository())
        .environmentObject(StockRepository(api: AlphaVantageAPI()))
        .environment(\.managedobjectContext,context)
    // Use a UIHostingController as window root view controller.
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)            
        window.rootViewController = UIHostingController(rootView: contentView)
        self.window = window
        window.makeKeyAndVisible()
    }
}

在我的 MainTabView 中,我创建了一个这样的选项卡:

struct MainTabView: View {
    @EnvironmentObject var database: DatabaseRepository
    @EnvironmentObject var userRepository: UserRepository
    @EnvironmentObject var stockRepository: StockRepository
    var viewmodel = MainTabviewmodel()
    init() {
        UITabBar.appearance().barTintColor = UIColor(named: "primary")
    }
    var body: some View {
        TabView {
            HomeView(viewmodel: Homeviewmodel(databaseRepository: database,userRepository: userRepository,stockRepository: stockRepository))
                .tabItem {
                    Image("account_balance_wallet")
                        .renderingMode(.template)
                    Text("Home")
                }.tag(0)
            ...
        }.accentColor(.white)
    }
}

在我的 HomeVM 中,我有这个代码

class Homeviewmodel: ObservableObject {
    @Published var watchingVMs = [EquityPreviewCellviewmodel]()
    

    private let databaseRepository: DatabaseRepositoryProtocol
    private let userRepository: UserRepositoryProtocol
    private let stockRepository: StockRepositoryProtocol
    

    init(databaseRepository: DatabaseRepositoryProtocol,userRepository: UserRepositoryProtocol,stockRepository: StockRepositoryProtocol) {
        self.databaseRepository = databaseRepository
        self.userRepository = userRepository
        self.stockRepository = stockRepository
        self.bind()
    }
    func bind() {
        let allorders = databaseRepository
            .allOrder(userID: userRepository.userID).share()
        allorders
            .assertNoFailure()
            .compactMap({orders in
                return orders?.reduce([Order](),{ finalOrders,nextOrder in
                    var orders = [Order](finalOrders)
                    if !finalOrders.contains(where: { $0.symbol == nextOrder.symbol }) {
                        orders.append(nextOrder)
                    }
                    return orders
                }).map({EquityPreviewCellviewmodel(order: $0,stockRepository: self.stockRepository,userRepository: self.userRepository,dataBaseRepository: self.databaseRepository)})
            })
            .receive(on: dispatchQueue.main)
            .assign(to: &self.$watchingVMs)

我的我启动我的应用程序我有这个崩溃:

线程 1:EXC_BAD_INSTRUCTION(代码=EXC_I386_INVOP,子代码=0x0)

enter image description here

我认为我不能很好地使用 environmentObject,但我不明白为什么。

解决方法

可能是你想要的

.assign(to: \.watchingVMs,on: self)

或(使用弱自我)

.sink { [weak self] value in
   self?.watchingVMs = value
}

更新:将本地 allorders 移动到属性以保持引用有效

func bind() {
    let allorders = databaseRepository
        .allOrder(userID: userRepository.userID).share()
    allorders    // << this one is destroyed on quit from bind
,

我认为您可能需要将此添加到 MainTabView

@Environment(\.managedObjectContext) var managedObjectContext

由于我们使用环境修饰符进行设置,因此您可能会在简单的 @FetchRequest 之上执行保存、删除和其他一些任务。

@Asperi 建议的更改也是完全正确的,应该为此问题添加。这是因为方法签名不同:

.assign(to: \.watchingVMs,on: self)

也可能存在导致 EXC_BAD_ACCESS 的线程问题,这里可能值得检查同步问题,即在同一个主队列上执行提取请求。