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

SwiftUI Core Data列表后台返回后删除崩溃问题求助

问题描述

我已实现Core Data并将所有数据加载至SwiftUI的List中,添加、删除功能原本均可正常运行。但当App进入后台(变为非活跃状态)再重新打开后,删除列表行时应用会崩溃,报错信息为:Thread 1: "An NSManagedObjectContext cannot delete objects in other contexts."

相关代码如下:

HistoryView 代码

struct HistoryView: View {
  @State private var history: [HistoryList] = [HistoryList]()

  let coreDM: CoreDataManager
   
  var dateFormatter: DateFormatter {
    let formatter = DateFormatter()
    formatter.dateFormat = "MM-dd-yyyy HH:mm"
    return formatter
  }
   
  private func populateHistory(){
    history = coreDM.getAllHistory()
  }
   
  var body: some View {
    NavigationView{
      VStack {
        if !history.isEmpty {
          List {
            ForEach(history, id: \.self) { historyList in
              HStack {
                Text(dateFormatter.string(from: historyList.dateFlash ?? Date(timeIntervalSinceReferenceDate: 0)))
                Text("\(historyList.timerFlash)s")
                  .multilineTextAlignment(.trailing)
                  .frame(maxWidth: .infinity, alignment: .trailing)
              }
            }.onDelete(perform: { indexset in
              indexset.forEach { index in
                let history = history[index]
                coreDM.deleteHistory(history: history)
                populateHistory()
              }
            })
          }.refreshable {
            populateHistory()
            print("## Refresh History List")
          }
        } else {
          Text("History Flashlight is Empty")
        }
      }
      .onAppear {
        populateHistory()
        print("OnAppear")
      }
    }.navigationTitle("History Flashlight")
      .navigationBarTitleDisplayMode(.inline)
  }
}

struct HistoryView_Previews: PreviewProvider {
  static var previews: some View {
    HistoryView(coreDM: CoreDataManager())
  }
}

CoreDataManager 代码

import CoreData

class CoreDataManager {
    
    let persistentContainer: NSPersistentContainer
    
    init(){
        persistentContainer = NSPersistentContainer(name: "DataModel")
        persistentContainer.loadPersistentStores { (description , error) in
            if let error = error {
                fatalError("Core Data Store failed \(error.localizedDescription)")
            }
        }
    }
    
    func saveHistory(timeFlash: Int, dateFlash: Date) {
        let history = HistoryList(context: persistentContainer.viewContext)
        history.timerFlash = Int16(timeFlash)
        history.dateFlash = dateFlash
        
        do {
            try persistentContainer.viewContext.save()
        } catch {
            print("failed to save \(error)")
        }
    }
    
    func getAllHistory() -> [HistoryList] {
        
        let fetchRequest: NSFetchRequest<HistoryList> = HistoryList.fetchRequest()
        
        do {
            return try persistentContainer.viewContext.fetch(fetchRequest)
        } catch {
            return []
        }
    }
    
    func deleteHistory(history: HistoryList) {
        persistentContainer.viewContext.delete(history)
        
        do {
            try persistentContainer.viewContext.save()
        } catch {
            persistentContainer.viewContext.rollback()
            print("Failed to save context \(error)")
        }
    }
}

public extension NSManagedObject {

    convenience init(context: NSManagedObjectContext) {
        let name = String(describing: type(of: self))
        let entity = NSEntityDescription.entity(forEntityName: name, in: context)!
        self.init(entity: entity, insertInto: context)
    }
}
问题原因
  1. 托管对象的上下文关联失效:当App进入后台时,系统可能会重置Core Data的持久化存储或NSManagedObjectContext实例。你保存在@State变量里的HistoryList对象,仍然关联着旧的上下文实例。重新打开App后,调用deleteHistory时,尝试用当前新的上下文去删除属于旧上下文的对象,就会触发跨上下文操作的错误。
  2. @State数据未及时更新:@State中的history数组在App前后台切换后,没有重新获取关联当前上下文的最新对象,依然持有失效的旧托管实例。
解决方案

推荐使用SwiftUI官方推荐的@FetchRequest来绑定Core Data数据,它会自动处理上下文生命周期和数据更新,包括前后台切换的场景:

修改后的HistoryView代码

struct HistoryView: View {
    // 用@FetchRequest自动获取并监听Core Data数据,指定排序规则
    @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \HistoryList.dateFlash, ascending: false)])
    private var history: FetchedResults<HistoryList>

    let coreDM: CoreDataManager
   
    var dateFormatter: DateFormatter {
        let formatter = DateFormatter()
        formatter.dateFormat = "MM-dd-yyyy HH:mm"
        return formatter
    }
   
    var body: some View {
        NavigationView{
            VStack {
                if !history.isEmpty {
                    List {
                        ForEach(history, id: \.self) { historyList in
                            HStack {
                                Text(dateFormatter.string(from: historyList.dateFlash ?? Date(timeIntervalSinceReferenceDate: 0)))
                                Text("\(historyList.timerFlash)s")
                                    .multilineTextAlignment(.trailing)
                                    .frame(maxWidth: .infinity, alignment: .trailing)
                            }
                        }.onDelete(perform: { indexset in
                            indexset.forEach { index in
                                let historyItem = history[index]
                                coreDM.deleteHistory(history: historyItem)
                                // @FetchRequest会自动刷新列表,无需手动调用populateHistory
                            }
                        })
                    }.refreshable {
                        // 刷新操作:重新执行fetch以确保数据最新
                        do {
                            try history.managedObjectContext?.fetch(HistoryList.fetchRequest())
                        } catch {
                            print("刷新历史记录失败:\(error)")
                        }
                        print("## Refresh History List")
                    }
                } else {
                    Text("History Flashlight is Empty")
                }
            }
        }.navigationTitle("History Flashlight")
          .navigationBarTitleDisplayMode(.inline)
    }
}

struct HistoryView_Previews: PreviewProvider {
    static var previews: some View {
        HistoryView(coreDM: CoreDataManager())
    }
}

如果坚持手动管理数据,可添加前台通知监听,在App回到前台时强制刷新数据:

// 在HistoryView的body中添加
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
    populateHistory()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 01:55:23