SwiftUI:未调用@StateObject deinit?

SwiftUI: @StateObject deinit NOT called?

我有以下代码:

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
        animation: .default)
    private var items: FetchedResults<Item>

    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    NavigationLink {
                        Text("Item at \(item.timestamp!, formatter: itemFormatter)")
                    } label: {
//                        Text(item.timestamp!, formatter: itemFormatter)
                        ItemCellView(model: ItemCellViewModel(item: item))
                    }
                }
                .onDelete(perform: deleteItems)
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    EditButton()
                }
                ToolbarItem {
                    Button(action: addItem) {
                        Label("Add Item", systemImage: "plus")
                    }
                }
            }
            Text("Select an item")
        }
    }

    private func addItem() {
        withAnimation {
            let newItem = Item(context: viewContext)
            newItem.timestamp = Date()

            do {
                try viewContext.save()
            } catch {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }

    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            offsets.map { items[[=10=]] }.forEach(viewContext.delete)

            do {
                try viewContext.save()
            } catch {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
}

private let itemFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateStyle = .short
    formatter.timeStyle = .medium
    return formatter
}()

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}


struct ItemCellView: View {
    
    @StateObject var model:ItemCellViewModel
    
    var body: some View {
        
        Text(model.item.timestamp!, formatter: itemFormatter)
            .foregroundColor(.blue)
    }
    
}

class ItemCellViewModel: ObservableObject {
    
    @Published var item:Item
    
    init(item:Item) {
        self.item = item
    }
    
    deinit {
        print("ItemCellViewModel EDINIT \(self)")
    }
    
}

它画了这个:

问题:

ItemCellViewModel 在我滑动删除项目后没有调用 deinit。 有人能告诉我为什么 ItemCellViewModel 即使在 ItemCellView 消失后仍然存在吗?

这是我正在使用的代码库的简化版本。当视图被用户“删除”时,我需要该模型消失。为什么 SwiftUI 保持 ItemCellViewModel 左右?

View 实际上并没有被删除(只是从可见区域中删除),因为 List 缓存了一定数量的视图(可见区域 + ~2),而 StateObject 是视图的持久存储,它保持其状态。所以观察到的行为是 by-design.

去掉视图模型对象,在 SwiftUI 中我们使用值类型,View 结构是 SwiftUI 用来代表我们创建和更新 UIKit/AppKit 视图的视图模型。在 SwiftUI Essentials WWDC 2019 中了解这一点。此外,您也不能将 ObservableObject 嵌套在 ObservableObject 中。要解决此问题,请将 ItemCellView 更改为:

struct ItemCellView: View {
    
    @ObservedObject var item: Item
    
    var body: some View {
        
        Text(item.timestamp!, formatter: itemFormatter)
            .foregroundColor(.blue)
    }
    
}