You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

一对多关系中子项删除导致SwiftData崩溃问题求助

SwiftData一对多关系删除子项导致崩溃的解决方案

问题原因

崩溃的核心在于两点:

  1. 原模型未定义双向关联,SwiftData无法自动维护父对象(Plan)的todos数组与子对象(Todo)的引用关系,删除Todo后,Plan的todos数组仍保留已被标记为删除的实例
  2. 视图直接访问已被删除的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 14:36:12