SwiftData健身应用问题:Workout对象提前实例化且无法销毁
基于SwiftData的健身应用实例化与对象销毁问题解决
问题背景
我开发了一款基于SwiftData的健身应用,用户点击“Start Workout”按钮后会跳转到CurrentWorkoutView,在该视图中实例化Workout对象,用户后续可选择保存或取消训练。目前遇到两个问题:
- 应用刚启动时,
Workout对象就已被实例化; - 用户保存或取消训练后,
Workout对象无法被销毁。
简化代码
HomeView.swift
... NavigationLink("Start Workout") { CurrentWorkoutView() } ...
CurrentWorkoutView.swift
@Environment(\.dismiss) var dismiss @Environment(\.modelContext) var context @Bindable var workout: Workout = Workout() var body: some View { ... .toolbar { ToolbarItem(placement: .confirmationAction) { Button("Save") { workout.end = Date.now context.insert(workout) dismiss() } } ToolbarItem(placement: .cancellationAction) { Button("Cancel") { context.delete(workout) try! context.save() dismiss() } } } }
我曾尝试将workout设为可空类型,但由于被@Bindable修饰无法实现,而该对象必须为Bindable才能支持标题、描述等内容的编辑。
解决方案
1. 延迟Workout对象实例化
不要在CurrentWorkoutView的属性定义处直接初始化Workout,通过初始化方法或懒加载确保仅当视图被加载(用户点击跳转后)才创建对象:
修改CurrentWorkoutView:
@Environment(\.dismiss) var dismiss @Environment(\.modelContext) var context @Bindable var workout: Workout // 通过初始化方法创建实例,仅在视图初始化时执行 init() { self.workout = Workout() }
这样应用启动时不会提前创建Workout,只有用户进入CurrentWorkoutView时才会实例化对象。
2. 正确处理取消时的对象销毁
当前取消按钮调用context.delete(workout)无效,因为未保存的Workout还未被加入modelContext。只需放弃对象引用,ARC会自动回收:
修改取消按钮逻辑:
ToolbarItem(placement: .cancellationAction) { Button("Cancel") { // 仅当对象已被上下文管理时才执行删除 if workout.isPersistent { context.delete(workout) try? context.save() } dismiss() } }
同时优化保存按钮的错误处理(替换强制try!):
Button("Save") { workout.end = Date.now context.insert(workout) do { try context.save() } catch { print("保存训练失败: \(error.localizedDescription)") } dismiss() }
3. 可选类型配合@Bindable的实现方案
如果需要用可选类型,可通过@State包裹可选对象,再通过计算属性提供@Bindable实例:
@Environment(\.dismiss) var dismiss @Environment(\.modelContext) var context @State private var workout: Workout? = nil @Bindable var bindableWorkout: Workout { guard let existingWorkout = workout else { let newWorkout = Workout() self.workout = newWorkout return newWorkout } return existingWorkout } var body: some View { VStack { // 用bindableWorkout绑定编辑控件 TextField("训练标题", text: $bindableWorkout.title) // ...其他编辑内容 } .onAppear { // 视图出现时才实例化对象 if workout == nil { workout = Workout() } } .toolbar { ToolbarItem(placement: .confirmationAction) { Button("Save") { guard let workout = workout else { return } workout.end = Date.now context.insert(workout) do { try context.save() } catch { print("保存失败: \(error)") } dismiss() } } ToolbarItem(placement: .cancellationAction) { Button("Cancel") { guard let workout = workout else { dismiss() return } if workout.isPersistent { context.delete(workout) try? context.save() } // 置空引用触发ARC回收 self.workout = nil dismiss() } } } }
这种方式既实现了延迟实例化,又能在取消时通过置空引用销毁对象,同时满足绑定编辑的需求。
内容的提问来源于stack exchange,提问作者Thiago Dias
相关产品推荐
相关产品推荐

