SwiftUI:@ObservedObject 与 @StateObject 子视图性能?

Posted

技术标签:

【中文标题】SwiftUI:@ObservedObject 与 @StateObject 子视图性能?【英文标题】:SwiftUI: @ObservedObject vs @StateObject child view performance? 【发布时间】:2021-07-23 20:35:03 【问题描述】:

我有以下看法:

struct Menu: View 

    let sctions:[TestSection] = [

        TestSection(id: 0, name: "One", items: [
            ListItem(id: 0, name: "1"),
            ListItem(id: 1, name: "2"),
            ListItem(id: 2, name: "3"),
            ListItem(id: 3, name: "4")
        ]),

        TestSection(id: 1, name: "Two", items: [
            ListItem(id: 4, name: "4"),
            ListItem(id: 5, name: "5"),
            ListItem(id: 6, name: "6"),
            ListItem(id: 7, name: "7")
        ]),

        TestSection(id: 2, name: "Three", items: [
            ListItem(id: 8, name: "8"),
            ListItem(id: 9, name: "9"),
        ])
    ]

    var body: some View 

        NavigationView 
            List 
                ForEach(sctions)  section in

                    Section(header: Text(section.name)) 

                        ForEach(section.items)  item in
                            TestCell(item: item)
                        

                    
                

            
            .listStyle(.plain)
            .navigationBarTitle("Title")
        
    








struct TestCell: View 
    
    @ObservedObject private var model:ItemModel
    
    let item:ListItem
    
    init(item:ListItem) 
        self.item = item
        self.model = ItemModel(itemId:item.id)
    
    
    var body: some View 
        
        Text("item: \(item.name)")
        
        
    
    



class ItemModel:ObservableObject 
    
    @Published var someProperty:Int
    
    let itemId:Int
    
    init(itemId:Int) 
        self.itemId = itemId
        self.someProperty = 0
    
    

我正在尝试从模型层的角度决定如何在 SwiftUI 中处理子视图。

一个选项是子视图中的@ObservedObject。父级创建模型并将其设置在子级上,或者父级在子级上传递一个属性,然后允许子级在其初始化程序中初始化模型:

init(item:ListItem) 
    self.item = item
    self.model = ItemModel(itemId:item.id). // <<---- HERE

然后看看这个,我想知道这是否不如在子视图中使用@StateObject 来管理其模型的生命周期。

然后我尝试了这个:

struct TestCell: View 

    @StateObject var model = ItemModel() // <<-- error "Missing argument for parameter 'itemId' in call"

    let item:ListItem

    var body: some View 

        Text("item: \(item.name)")


    


其中,显然显示了错误:

我不确定在使用@StateObject 方法时如何在TestCell 中初始化ItemModel

我的问题是:

在这种类型的场景中,我希望每个单元都有自己的模型(以处理与模型相关的逻辑,例如进行网络调用、更新模型层等...)我应该创建模型,因为我在创建单元格期间实例化父级中的单元格,并将其设置在单元格上 (@ObservedObject)?

或者单元格是否应该为其模型使用@StateObject,如果是,我如何让模型在需要参数时正确初始化自己,例如let itemId:Int

【问题讨论】:

【参考方案1】:

不推荐您的第一个选项,因为

init(item:ListItem) 
    self.item = item
    self.model = ItemModel(itemId:item.id). // <<---- HERE

在视图中创建观察对象是不安全的

https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

您会在 SO 中找到许多关于 Views 不可靠的问题。您还会发现那些告诉人们这样做的人,但您会发现它存在问题。

如果您使用@StateObject var model: ItemModel = ItemModel(),根据 Apple 的规定,这是在 View 中创建 ObservableObject 的唯一例外

然后切换

let itemId:Int 

在您的ItemModel

var itemId:Int = 0

然后在TestCell 中添加到最外层的View

.onAppear()
    model.itemId = item.id

或者根据您的 ParentView 及其 ViewModel,您可以在 class ViewModel 中创建 ObservableObjects 并将其作为参数传递给

@ObservedObject private var model:ItemModel

重要的是,解决方法考虑到不应在View 中创建ObservableObject@StateObject 除外。 SwiftUI 保留在认为有必要时重新创建任何View 的权利。

【讨论】:

那么,@StateObject 根本不被视为驱动单元的选项? 不,StateObject 是最好的选择。当我提供选项时,我不知何故跳过了这一点。 StateObject 是在 View 中初始化 ObservableObject 的唯一保存方法。 好的,所以如果我在视图中初始化对象,那么 StateObject 是首选方式。如果我想从父视图传递它,那么 ObservedObject 是要走的路吗? 这取决于我在大多数情况下更喜欢 EnvironmentObject,但其他人更喜欢 ObservedObject。

以上是关于SwiftUI:@ObservedObject 与 @StateObject 子视图性能?的主要内容,如果未能解决你的问题,请参考以下文章

极简示例揭示 SwiftUI 中 @ObservedObject 与 @StateObject 状态的关键区别

UITextView 的 SwiftUI Wrapper 未更新 ObservedObject

SwiftUI - 如何使用 ObservedObject 或 EnvironmentObject 存储 GeometryReader 数据?

SwiftUI ObservedObject 不更新父视图

通用结构 'ObservedObject' 要求 'Video' 符合 SwiftUI 中的 'ObservableObject'

SwiftUI:为啥 ObservedObject 在 AppDelegate 中不起作用?