一对多关系中子项删除导致SwiftData崩溃问题求助
SwiftData一对多关系删除子项导致崩溃的解决方案
问题原因
崩溃的核心在于两点:
- 原模型未定义双向关联,SwiftData无法自动维护父对象(
Plan)的todos数组与子对象(Todo)的引用关系,删除Todo后,Plan的todos数组仍保留已被标记为删除的实例 - 视图直接访问已被删除的
Todo对象的summary属性,触发已释放对象访问崩溃
解决方案
1. 修正模型的双向关联定义
为SwiftData模型添加明确的双向关系,并配置删除规则,让框架自动管理关联对象的生命周期:
@Model final class Plan: Codable { // 配置一对多关系,删除子项时自动从数组移除 @Relationship(deleteRule: .nullify, inverse: \Todo.plan) var todos: [Todo] = [] enum CodingKeys: String, CodingKey { case todos } init() {} required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) todos = try container.decode([Todo].self, forKey: .todos) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(todos, forKey: .todos) } } @Model final class Todo: Codable { var summary: String // 添加反向关联到父对象Plan var plan: Plan? enum CodingKeys: String, CodingKey { case summary, plan } init(summary: String) { self.summary = summary } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) summary = try container.decode(String.self, forKey: .summary) plan = try container.decodeIfPresent(Plan.self, forKey: .plan) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(summary, forKey: .summary) try container.encodeIfPresent(plan, forKey: .plan) } }
2. 视图添加安全访问过滤
在PlanView中过滤掉已被标记为删除的对象,避免无效访问:
struct PlanView: View { @Query private var plans: [Plan] @Environment(\.modelContext) private var modelContext var body: some View { NavigationStack { ZStack { if let currentPlan = plans.first { // 过滤已删除的Todo List(currentPlan.todos.filter { !$0.isDeleted }) { todo in Section { Text("\(todo.summary)") } } } } } } }
3. 保持删除操作的上下文一致性
现有删除代码无需修改,但配合修正后的模型,SwiftData会自动更新父对象的关联数组:
private func deleteItems(offsets: IndexSet) { withAnimation { offsets.forEach { index in modelContext.delete(todos[index]) } } }
内容的提问来源于stack exchange,提问作者erotsppa
相关产品推荐
相关产品推荐

