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) } }
问题原因
- 托管对象的上下文关联失效:当App进入后台时,系统可能会重置Core Data的持久化存储或
NSManagedObjectContext实例。你保存在@State变量里的HistoryList对象,仍然关联着旧的上下文实例。重新打开App后,调用deleteHistory时,尝试用当前新的上下文去删除属于旧上下文的对象,就会触发跨上下文操作的错误。 - @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
相关产品推荐
相关产品推荐

