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

SwiftData中如何实现Workout模型的深拷贝?

解决SwiftData中Workout实例的深拷贝问题

SwiftData的@Model对象是Core Data托管对象的封装,关联的数组(比如Workout的exercises)存的是对象引用,直接复制基础属性不会自动创建关联对象的副本,所以得手动递归创建每个层级的新实例。

1. 先明确你的@Model类结构(示例)

import SwiftData

@Model
class Workout {
    var name: String
    var date: Date
    @Relationship(deleteRule: .cascade) var exercises: [Exercise] = []
    
    init(name: String, date: Date) {
        self.name = name
        self.date = date
    }
}

@Model
class Exercise {
    var name: String
    @Relationship(deleteRule: .cascade) var sets: [Set] = []
    
    init(name: String) {
        self.name = name
    }
}

@Model
class Set {
    var weight: Double
    var reps: Int
    
    init(weight: Double, reps: Int) {
        self.weight = weight
        self.reps = reps
    }
}

2. 给每个模型添加深拷贝方法

通过递归的方式,为Workout、Exercise分别创建深拷贝逻辑,确保每个层级的对象都是新实例:

extension Workout {
    func deepCopy(in context: ModelContext) -> Workout {
        // 复制Workout的基础属性
        let copiedWorkout = Workout(name: self.name, date: self.date)
        // 必须先插入上下文,否则后续关联Exercise会出错
        context.insert(copiedWorkout)
        
        // 遍历原Exercise,逐个深拷贝并关联到新Workout
        for exercise in self.exercises {
            let copiedExercise = exercise.deepCopy(in: context)
            copiedWorkout.exercises.append(copiedExercise)
        }
        
        return copiedWorkout
    }
}

extension Exercise {
    func deepCopy(in context: ModelContext) -> Exercise {
        let copiedExercise = Exercise(name: self.name)
        context.insert(copiedExercise)
        
        // 遍历原Set,创建新实例并关联到新Exercise
        for set in self.sets {
            let copiedSet = Set(weight: set.weight, reps: set.reps)
            context.insert(copiedSet)
            copiedExercise.sets.append(copiedSet)
        }
        
        return copiedExercise
    }
}

3. 在视图中调用拷贝方法

假设你已经拿到选中的Workout实例和ModelContext,直接调用方法即可:

@Environment(\.modelContext) private var modelContext
@State private var selectedWorkout: Workout?

// 按钮点击触发拷贝
func copySelectedWorkout() {
    guard let workout = selectedWorkout else { return }
    let copiedWorkout = workout.deepCopy(in: modelContext)
    // 可选:给拷贝后的 workout 加个标识,避免和原实例混淆
    copiedWorkout.name = "\(copiedWorkout.name) (副本)"
}

关键注意点

  • 所有新创建的@Model对象必须插入到ModelContext中,否则关联关系无法生效,也不会被持久化到存储中。
  • 必须递归处理每个层级的关联对象,不能直接复用原对象的引用,否则只是复制了关联关系,并没有真正创建副本。
  • 注意@Relationship的删除规则,示例中用的.cascade,删除原Workout会连带删除关联的Exercise和Set,但拷贝后的对象是独立的,不受原对象操作影响。

内容的提问来源于stack exchange,提问作者Milan Labus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 19:52:32