异步存储Core Data时实体关联关系失效问题求助
Core Data异步处理大文件时关联关系失效问题修复
问题描述
我正在开发一款iOS Swift应用,数据模型包含GameModule和GameModuleFile两个实体,二者为一对多关联。从磁盘加载ZIP文件并解压后,将文件数据存入GameModuleFile实体并关联至最新的GameModule。同步执行时一切正常,但异步执行时,关联关系未被存入Core Data且无报错;有趣的是,小ZIP文件(数百个文件)关联正常,大文件(数千个文件)则失效。
问题代码
import SwiftUI import UniformTypeIdentifiers struct GameModuleListView: View { @Environment(\.managedObjectContext) var moc @FetchRequest(sortDescriptors: [SortDescriptor(\.order)]) var gameModules: FetchedResults<GameModule> @State private var loadModuleTrigger = false var body: some View { NavigationView { List { ForEach(gameModules, content: GameModuleRow.init) } } .navigationViewStyle(StackNavigationViewStyle()) .fileImporter(isPresented: $loadModuleTrigger, allowedContentTypes: [.zip], allowsMultipleSelection: false, onCompletion: loadModule) } func loadModule(res: Result<[URL], Error>) { let url = try! res.get()[0] DispatchQueue.global(qos: .userInitiated).async { try? gameModules[gameModules.count - 1].loadFiles(context: moc, url: url) } } } import ZIPFoundation extension GameModule { func loadFiles(context moc: NSManagedObjectContext, url: URL) throws { let archive = try GameModule.getArchiveFromUrl(url: url) for entry in archive { if entry.path.last! != "/" { _ = try? archive.extract(entry) { data in let file = GameModuleFile(context: moc) file.filePath = entry.path file.blob = data file.ownerModule = self } } } try? moc.save() } }
修复后代码
import SwiftUI import UniformTypeIdentifiers struct GameModuleListView: View { @Environment(\.managedObjectContext) var moc @FetchRequest(sortDescriptors: [SortDescriptor(\.order)]) var gameModules: FetchedResults<GameModule> @State private var loadModuleTrigger = false var body: some View { NavigationView { List { ForEach(gameModules, content: GameModuleRow.init) } } .navigationViewStyle(StackNavigationViewStyle()) .fileImporter(isPresented: $loadModuleTrigger, allowedContentTypes: [.zip], allowsMultipleSelection: false, onCompletion: loadModule) } func loadModule(res: Result<[URL], Error>) { moc.perform { let newModule = try! GameModule(moc) let url = try! res.get()[0] try? newModule.loadFiles(context: moc, url: url) try? moc.save() } } } import ZIPFoundation extension GameModule { func loadFiles(context moc: NSManagedObjectContext, url: URL) throws { let archive = try GameModule.getArchiveFromUrl(url: url) for entry in archive { if entry.path.last! != "/" { _ = try? archive.extract(entry) { data in let file = GameModuleFile(context: moc) file.filePath = entry.path file.blob = data file.ownerModule = self } } } } }
关键修复点说明
- 线程安全问题解决:Core Data上下文并非线程安全,原代码直接在全局队列操作UI线程的上下文,导致数据竞争。修复后使用
moc.perform确保所有Core Data操作都在上下文所属线程执行。 - 保存时机调整:将
moc.save()移至perform闭包内,保证所有文件实体创建和关联操作完成后再统一保存上下文,避免部分数据未持久化。 - 托管对象访问规范:原代码跨线程访问
gameModules中的托管对象存在风险,修复后在perform闭包内创建新的GameModule实例,符合Core Data线程访问规则。
内容的提问来源于stack exchange,提问作者Honza Skýpala
相关产品推荐
相关产品推荐

