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

SwiftData中使用@Relationship(.cascade)级联删除模型时崩溃的问题咨询

SwiftData中使用@Relationship(.cascade)级联删除模型时崩溃的问题咨询

我最近在SwiftData里测试@Relationship(deleteRule: .cascade)级联删除的特性,结果遇到了一个奇怪的崩溃,折腾了好半天没找到原因,来请教下各位大佬。

崩溃现象与错误信息

崩溃时抛出的错误信息如下:

Cannot fulfill future for PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(backing: SwiftData.PersistentIdentifier.PersistentIdentifierBacking.managedObjectID(0x9dde110179619224 x-coredata://D4C9A4D3-8E7F-49B2-A07B-FD91A8906552/Child/p8)), backing: SwiftData.PersistentIdentifier.PersistentIdentifierBacking.managedObjectID(0x9dde110179619224 x-coredata://D4C9A4D3-8E7F-49B2-A07B-FD91A8906552/Child/p8)) without a context SwiftData._FullFutureBackingData<SwiftData_Bootcamp.Child>

触发崩溃的场景是:我在DetailView中删除一个Adult实例,然后调用dismiss()返回列表页的时候,App直接崩溃了。

我的数据模型结构

我定义了两个简单的模型Adult和Child,均继承自Person基础类,它们的关联关系如下:

// Adult
@Model
class Adult: Person {
    var id: UUID = UUID()
    var name: String
    var age: Int
    @Relationship(deleteRule: .cascade) var children: [Child] = []
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

// Child
@Model
class Child: Person {
    var id: UUID = UUID()
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

预期行为

我设置了@Relationship(deleteRule: .cascade),所以预期删除一个Adult实例时,所有关联的Child实例会被自动级联删除,不需要手动处理。

崩溃触发的具体场景

崩溃发生在DetailView中执行删除操作后调用dismiss()的瞬间,核心代码逻辑如下:

struct DetailView: View {
    @Environment(\.modelContext) private var modelContext
    @Environment(\.dismiss) private var dismiss
    let adult: Adult
    
    private func delete() {
        do {
            modelContext.delete(adult)
            try modelContext.save()
        } catch {
            print(error.localizedDescription)
        }
        dismiss()
    }
}

完整UI代码(供参考)

ContentView

struct ContentView: View {
    @Query(animation: .default) private var adults: [Adult]
    @Query(animation: .default) private var children: [Child]
    @State private var showForm: Bool = false
    
    var body: some View {
        NavigationStack {
            List {
                if adults.isEmpty {
                    Text("无数据")
                } else {
                    ForEach(adults) { adult in
                        NavigationLink(value: adult) {
                            row(adult)
                        }
                    }
                }
            }
            .navigationTitle("Persons")
            .toolbar {
                ToolbarItem(content: createButton)
            }
            .navigationDestination(
                isPresented: $showForm,
                destination: { FormView() }
            )
            .navigationDestination(
                for: Adult.self,
                destination: { adult in DetailView(adult: adult) }
            )
        }
    }
}

#Preview {
    ContentView()
        .modelContainer(for: Adult.self, inMemory: true)
        .modelContainer(for: Child.self, inMemory: true)
}

extension ContentView {
    private func row(_ data: Adult) -> some View {
        HStack {
            Text(data.name)
            Spacer()
            Text("孩子: \(data.children.count)")
        }
    }
    
    private func createButton() -> some View {
        Button("创建", systemImage: "plus") {
            showForm.toggle()
        }
    }
}

DetailView 完整代码

struct DetailView: View {
    @Environment(\.modelContext) private var modelContext
    @Environment(\.dismiss) private var dismiss
    let adult: Adult
    
    var body: some View {
        List {
            Section("基本信息") {
                LabeledContent("姓名", value: adult.name)
                LabeledContent("岁数", value: adult.age, format: .number)
            }
            Section("娃") {
                if adult.children.isEmpty {
                    Text("没娃")
                } else {
                    ForEach(adult.children) { child in
                        LabeledContent(child.name) {
                            Text("\(child.age)岁")
                        }
                    }
                }
            }
            Button("删除", role: .destructive, action: delete)
        }
    }
}

#Preview {
    DetailView(adult: .init(name: "noah", age: 5))
}

extension DetailView {
    private func delete() {
        do {
            modelContext.delete(adult)
            try modelContext.save()
        } catch {
            print(error.localizedDescription)
        }
        dismiss()
    }
}

FormView 完整代码

struct FormView: View {
    @Environment(\.modelContext) private var modelContext
    @Environment(\.dismiss) private var dismiss
    @State private var name: String = ""
    @State private var age: Int = 1
    
    var body: some View {
        Form {
            TextField("姓名", text: $name)
            Stepper("岁数", value: $age)
            Section {
                Button("创建", action: create)
                    .disabled(name.isEmpty)
            }
        }
    }
}

#Preview {
    FormView()
}

extension FormView {
    private func create() {
        let adult = Adult(name: name, age: age)
        let childCount = Int.random(in: 1...5)
        for _ in 0..<childCount {
            let child = Child(
                name: names.randomElement() ?? "二狗",
                age: Int.random(in: 0...16)
            )
            adult.children.append(child)
        }
        modelContext.insert(adult)
        do {
            try modelContext.save()
        } catch {
            print(error.localizedDescription)
        }
        dismiss()
    }
}

我的疑问

  1. 为什么使用.cascade删除规则会触发这个崩溃?是不是我误解了SwiftData中@Relationship(deleteRule:)的工作方式?
  2. 我该如何正确实现“删除Adult时自动删除所有关联Child”的功能,同时避免这个崩溃?

我已经反复看了苹果官方的@Relationship API文档,但可能漏掉了某些细节,希望各位能帮忙指点一下!

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 09:03:09