2

从我在视图中自己实例化对象时所读到的内容中,您应该使用@StateObject而不是@ObservedObject. 因为显然,如果您使用@ObservedObjectSwiftUI,SwiftUI 可能会随时决定将其丢弃并稍后重新创建它,这对于某些对象来说可能会很昂贵。但是如果你改用@StateObject它,那么显然 SwiftUI 知道不要把它扔掉。

我理解正确吗?

我的问题是,如何将其@StateObject传达给 SwiftUI?我问的原因是因为我制作了自己的 propertyWrapper,它将视图的属性连接到 firebase firestore 集合,然后开始监听实时快照。这是一个看起来像的例子:

struct Room: Model {
    @DocumentID
    var id: DocumentReference? = nil
    var title: String
    var description: String

    static let collectionPath: String = "rooms"
}

struct MacOSView: View {
    @Collection({ $0.order(by: "title") })
    private var rooms: [Room]
    
    var body: some View {
        NavigationView {
            List(rooms) { room in
                NavigationLink(
                    destination: Lazy(ChatRoom(room))
                ) {
                    Text(room.title)
                }
            }
        }
    }
}

内部的闭包@Collection是可选的,但可用于为集合构建更精确的查询。

现在这很好用。代码很有表现力,我得到了很好的实时更新数据。您可以看到,当用户单击房间标题时,导航视图将导航到该聊天室。聊天室是显示该房间中所有消息的视图。这是该代码的简化视图:

struct ChatRoom: View {
    @Collection(wait: true)
    private var messages: [Message]
    
    // I'm using (wait: true) to say "don't open a connection just yet,
    // because I need to give you more information that I can't give you yet"
    // And that's because I need to give @Collection a query
    // based on the room id, which I can only do in the initializer.

    init(_ room: Room) {
        _messages = Collection { query in
            query
                .whereField("room", isEqualTo: room.id!)
                .order(by: "created")
        }
    }
    
    var body: some View {
        List(messages) { message in
            MessageBubble(message: message)
        }
    }
}

但我注意到,每次用户以任何方式与 UI 交互时,SwiftUI 都会初始化一个新的消息集合。就像即使单击输入框以使其获得焦点一样。这是一个巨大的内存泄漏。我不明白为什么会发生这种情况,但是有没有办法告诉 SwiftUI 只初始化@Collection一次,就像它一样@StateObject

4

1 回答 1

2

@StateObject当您要定义该视图拥有并与其生命周期相关联的新的真实引用类型时使用。该对象将在第一次运行主体之前创建,并由 SwiftUI 存储在一个特殊的位置。如果重新创建 View 结构(例如,通过状态更改重新计算父 View 的主体),则将在属性上设置前一个对象而不是新对象。如果在主体更新期间视图不再初始化,则对象将被取消初始化。

当您将 a 传递给@StateObject子 View 时,在该子 View 中用于@ObservedObject在对象更改时重新计算子 View 的主体,就像父 View 的主体也将被重新计算一样,因为它使用了@StateObject. 如果您使用@ObservedObject的对象不是@StateObject父视图中的对象,那么由于没有视图拥有它,那么每次在主体更新期间视图初始化时,它都会丢失。此外,您在 View init 中创建的任何对象,例如您的 Collection,也会立即丢失。这些过多的堆分配可能会导致泄漏并减慢 SwiftUI。视图拥有的对象必须被包裹@StateObject以避免这种情况。

最后,不要@StateObject用于视图状态,当然也不要尝试用它们实现传统的“视图模型”模式。我们有@State并且@Binding为此。@StateObject仅适用于模型对象(如在您的域类型中:Book、Person 等)、加载器或提取器。

SwiftUI 中的 WWDC 2020 Data Essentials很好地解释了这一切。

于 2020-10-22T19:43:19.707 回答