【问题标题】:SwiftUI : Thread error when accessing EnvironmentObject access at runtimeSwiftUI:在运行时访问 EnvironmentObject 访问时出现线程错误
【发布时间】:2021-01-04 14:15:36
【问题描述】:

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

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)

我想我使用 environmentObject 不太好,但我不明白为什么。

【问题讨论】:

  • 我的解决方案有效吗?让我知道。

标签: swiftui swiftui-environment


【解决方案1】:

也许你想要

.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

【讨论】:

  • 不,两者都发生了同样的崩溃。即使是空的水槽崩溃
  • 即使在属性中移动它也只会在第一次运行时崩溃
【解决方案2】:

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

@Environment(\.managedObjectContext) var managedObjectContext

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

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

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

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

【讨论】:

  • 最后,是我的assertNoFailure 遇到了失败。最后,与环境对象无关。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多