为啥 SwiftUI @main App 中的实例化不创建 EnvironmentObject?

Posted

技术标签:

【中文标题】为啥 SwiftUI @main App 中的实例化不创建 EnvironmentObject?【英文标题】:Why Doesn't Instantiation In SwiftUI @main App Not Create An EnvironmentObject?为什么 SwiftUI @main App 中的实例化不创建 EnvironmentObject? 【发布时间】:2021-07-09 18:07:56 【问题描述】:

我正在使用 SwiftUI 和 @EnvironmentObjects。我正在使用 SwiftUI 应用程序生命周期。在这个文件中,我为 ListingRepository 创建了一个 @StateObjects 并使用 .envrionmentObjects() 将它附加到 ContentView()。

struct MyApp: App 
    // @EnvironmentObjects
    @StateObject private var listingRepository = ListingRepository()
    
    var body: some Scene 
        WindowGroup 
            ContentView()
                .environmentObject(listingRepository)
        
    

我的假设是我现在可以通过使用 @EnvironmentObject 包装器来访问 ListingRepository。但是,似乎我必须再次实例化它。下面是我的列表视图模型。我的第一次尝试如下所示。

class MarketplaceViewModel: ObservableObject 
    @EnvironmentObject var listingRepository: ListingRepository
    @Published var listingRowViewModels = [ListingRowViewModel]()
    
    private var cancellables = Set<AnyCancellable>()
    
    init() 
        listingRepository
            .$listings
            .receive(on: RunLoop.main)
            .map  listings in
                listings.map  listing in
                    ListingRowViewModel(listing: listing)
                
            
            .assign(to: \.listingRowViewModels, on: self)
            .store(in: &cancellables)
    

这引发了以下错误。

致命错误:未找到 ListingRepository 类型的 ObservableObject。一种 ListingRepository 的 View.environmentObject(_:) 可能作为 此视图的祖先。

第二个选项使用@Published 并修复错误。

class MarketplaceViewModel: ObservableObject 
    @Published var listingRepository = ListingRepository()
    @Published var listingRowViewModels = [ListingRowViewModel]()
    
    private var cancellables = Set<AnyCancellable>()
    
    init() 
        listingRepository
            .$listings
            .receive(on: RunLoop.main)
            .map  listings in
                listings.map  listing in
                    ListingRowViewModel(listing: listing)
                
            
            .assign(to: \.listingRowViewModels, on: self)
            .store(in: &cancellables)
    

我的问题如下。

    有必要像我一样实例化两次吗? 为什么@main App 中的实例化不起作用?

【问题讨论】:

【参考方案1】:

在您的@main App 中,您应该像这样定义您的环境对象:

struct MyApp: App 
    // @EnvironmentObjects
    var listingRepository = ListingRepository()
    
    var body: some Scene 
        WindowGroup 
            ContentView()
                .environmentObject(listingRepository)
        
    

然后将 ListingRepository 定义为:

class ListingRepository: ObservableObject 

    @Published var ...
    @Published var ...

    // Your code here

不要在 MarketPlaceViewModel 中将 ListingRepository 重新声明为 EnvironmentObject。

但是,请注意,将您的 ListingRepository 设为可观察对象并通过 MarketPlaceViewModel 从您的视图中访问该对象是不明智的。如果 MarketPlaceViewModel 用于填充应用中的视图,并且 MarketPlaceViewModel 从 ListingRepository 获取数据,则应将 MarketPlaceViewModel 设为 EnvironmentObject 而不是 ListingRepository。

SwiftUI 中的事情是您希望 EnvironmentObject 将更改发布到应用程序中的多个视图,以便这些视图可以重建自己。如果您在模型和视图之间使用视图模型,则视图模型应该是 EnvironmentObject。

【讨论】:

非常感谢您的回复。我正在关注谷歌推荐的教程。我会进一步调查。 你可能想看看这个教程youtube.com/watch?v=stSB04C4iS4或者这个youtube.com/watch?v=1QcekWFK2d8

以上是关于为啥 SwiftUI @main App 中的实例化不创建 EnvironmentObject?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能访问这个数组 ForEach 循环中的数据? SwiftUI

如何仅使用 SwiftUI 从@main App 访问 NSWindow?

SwiftUI - HealthKit 中的 DispatchQueue.main.async

SwiftUI - HealthKit 中的 DispatchQueue.main.async

为啥vue-cli打包后,存放vue实例的变量app被删除了?(其他js文件无法访问到app变量)

无法在 main() 方法中实例化字段(实例变量)。为啥??爪哇