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

SwiftUI+CoreData应用删除列表项后偶现EXC_BAD_ACCESS崩溃及优化问询

我来帮你逐一解决这些问题,结合你的代码给出具体的优化方案:

1. 实现CoreData列表自动刷新的最简方案

你之前手动调用fetchAll()的方式不仅繁琐,还引发了循环调用的崩溃问题。正确的做法是利用CoreData的上下文变化通知,让Model自动监听数据变更并更新列表:

修改Model.swift,移除tasks的didSet代码,改用Combine监听CoreData的通知:

import Foundation
import CoreData
import Combine

class Model: ObservableObject {
    private let context: NSManagedObjectContext
    @Published var tasks: [Task] = []
    private var cancellables = Set<AnyCancellable>()

    init(_ viewContext: NSManagedObjectContext) {
        self.context = viewContext
        // 监听CoreData上下文变化,自动刷新列表
        NotificationCenter.default.publisher(for: NSManagedObjectContext.objectsDidChangeNotification, object: context)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] _ in
                self?.fetchAll()
            }
            .store(in: &cancellables)
    }

    // 其他原有方法保持不变
}

这样一来,不管是添加、删除还是修改任务,CoreData都会发送通知触发fetchAll(),@Published修饰的tasks会自动通知View刷新,完全不需要手动调用刷新方法。

2. 代码优化与稳定性提升

你的崩溃问题根源在于tasks的didSet中调用fetchAll(),导致无限循环:fetchAll()修改tasks触发didSet,didSet又调用fetchAll(),反复执行最终引发线程错误。我们先解决这个核心问题,再做其他优化:

核心修复:移除循环调用

彻底删除tasks的didSet代码,就是你之前添加的这段:

didSet {
    DispatchQueue.main.async {
        self.fetchAll()
    }
}

其他优化点:

简化TaskRow的Toggle绑定

原来的自定义Binding可以直接用@ObservedObject的属性绑定,配合onChange更新时间戳:

// TaskRow.swift
Toggle("", isOn: $task.checked)
    .onChange(of: task.checked) { _ in
        task.updatedTimestamp = Date()
        model.save()
    }

简化addTask方法

用Task(context: context)创建的对象会自动加入上下文,不需要手动调用context.insert(task):

// Model.swift
func addTask(_ text: String) {
    let name = text.trimmingCharacters(in: .whitespacesAndNewlines)
    guard !name.isEmpty else { return }
    let task = Task(context: context)
    task.id = UUID()
    task.name = name
    task.creationTimestamp = Date()
    task.updatedTimestamp = Date()
    save()
}

保护Context不被外部修改

把Model中的context改为private let,避免外部代码意外修改上下文:

// Model.swift
private let context: NSManagedObjectContext

简化删除逻辑

onDelete可以直接通过索引获取任务并删除:

// TaskList.swift
private func onDelete(with indexSet: IndexSet) {
    indexSet.forEach { model.context.delete(model.tasks[$0]) }
    model.save()
}

3. 删除操作后的红线问题

这个红线是macOS早期SwiftUI版本的已知bug,在Xcode 11.x对应的SwiftUI中比较常见,属于List组件在删除项后渲染异常导致的残留分割线。

临时缓解方案:给List添加一个动态ID,当任务数量变化时强制List重新渲染:

// TaskList.swift
List(selection: $selection){
    // 原有代码保持不变
}
.id(model.tasks.count) // 新增这行

彻底解决的话,建议升级到兼容macOS 10.15的最新Xcode版本(比如Xcode 12.4),后续版本的SwiftUI已经修复了这个问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 20:57:36