SwiftUI:从CellView删除带非可选属性的Managed Object致崩溃
SwiftUI 删除Core Data托管对象时CellView崩溃问题解答
1. 为何timestamp为非可选类型时会崩溃?
Core Data中,托管对象被删除后会变成故障对象(Fault),此时它的内存数据会被清空以节省资源。对于非可选属性(比如timestamp: Date),Core Data无法返回一个合法的非可选值,直接访问就会触发崩溃;而可选属性访问时会返回nil,用if let判断就能避开崩溃。
2. 视图为何会尝试重绘已删除Item的CellView?
- 你用
@ObservedObject绑定了Item对象,当Item被删除后,SwiftUI的动画或视图更新机制可能存在短暂的时序差:列表还没来得及从FetchedResults中移除这个Item对应的CellView,视图就触发了重绘。 - 直接在CellView内部执行删除并保存上下文,和列表的
onDelete逻辑可能存在状态冲突,导致视图没有及时同步到最新的数据源状态,仍然持有已删除对象的引用。
3. 如何修复该问题?
给你两种可行的修复方案,都能保留CellView和@ObservedObject的用法:
方案一:在CellView中检查对象状态,避免访问已删除对象的属性
在CellView的body里先判断对象是否已删除,再渲染内容:
struct CellView: View { @Environment(\.managedObjectContext) private var viewContext @ObservedObject var item:Item var body: some View { HStack { // 先判断对象是否已删除,再访问属性 if !item.isDeleted { Text(item.timestamp, formatter: itemFormatter) } Button { withAnimation { viewContext.delete(item) try? viewContext.save() } } label: { Text("DELETE") .foregroundColor(.red) } .buttonStyle(.borderless) } } }
方案二:统一删除逻辑到父视图,让数据源自动同步
把删除动作通过闭包回调给父视图处理,利用@FetchRequest的自动更新特性,确保列表及时移除已删除的CellView:
- 修改CellView,添加删除回调闭包:
struct CellView: View { @ObservedObject var item:Item let onDelete: () -> Void // 新增回调闭包 var body: some View { HStack { Text(item.timestamp, formatter: itemFormatter) Button { withAnimation { onDelete() // 调用父视图的删除逻辑 } } label: { Text("DELETE") .foregroundColor(.red) } .buttonStyle(.borderless) } } }
- 在ContentView中传递删除逻辑:
ForEach(items) { item in NavigationLink { Text(item.timestamp, formatter: itemFormatter) } label: { CellView(item: item) { // 在父视图中执行删除 viewContext.delete(item) try? viewContext.save() } } }
这种方案更符合SwiftUI的数据流设计,父视图管理数据源,子视图只负责展示和触发动作,能彻底避免视图持有已删除对象的问题。
内容的提问来源于stack exchange,提问作者zumzum
相关产品推荐
相关产品推荐

