Rei*_*ner 3 multithreading core-data ios
注意:
\n这篇文章不适用,因为我实际上使用的是 CoreData。
\n在这篇文章中,最后一个答案建议在添加新对象之前获取新后台线程中的所有项目,但这是在我的代码中完成的。
\n这篇文章建议在保存项目的上下文之前对项目进行故障排除,但这也在我的代码中完成。
我的应用程序使用 CoreData 来存储名为 的对象shoppingItems。我编写了一个类CoreDataManager来初始化 CoreData,并且本质上有一个函数来覆盖当前存储的项目,以及一个函数来获取所有项目。这两个函数都在后台运行,即在单独的线程上运行。
这是我的代码(省略了不相关的部分)。
\n\n我在主线程上设置核心数据:
\n\nprivate lazy var persistentContainer: NSPersistentContainer = {\n let container = NSPersistentContainer(name: modelName)\n container.loadPersistentStores(completionHandler: { (storeDescription, error) in\n })\n return container\n}() \nRun Code Online (Sandbox Code Playgroud)\n\n这是写函数:
\n\nfunc overwriteShoppingItems(_ shoppingItems: Set<ShoppingItem>, completion: @escaping (Error?) -> Void) {\n let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)\n let viewContext = self.persistentContainer.viewContext\n backgroundContext.parent = viewContext\n backgroundContext.performAndWait {\n // Delete currently stored shopping items\n let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: CDEntityShoppingItem)\n do {\n let result = try backgroundContext.fetch(fetchRequest)\n let resultData = result as! [NSManagedObject]\n for object in resultData {\n backgroundContext.delete(object)\n }\n\n if !shoppingItems.isEmpty {\n // Save shopping items in managed context\n let cdShoppingItemEntity = NSEntityDescription.entity(forEntityName: CDEntityShoppingItem, in: backgroundContext)!\n for nextShoppingItem in shoppingItems {\n let nextCdShoppingItem = CDShoppingItem(entity: cdShoppingItemEntity, insertInto: backgroundContext)\n nextCdShoppingItem.name = nextShoppingItem.name\n }\n }\n\n let saveError = self.saveManagedContext(managedContext: backgroundContext)\n completion(saveError)\n } catch let error as NSError {\n completion(error)\n }\n }\n}\n\nfunc saveManagedContext(managedContext: NSManagedObjectContext) -> Error? {\n if !managedContext.hasChanges { return nil }\n do {\n try managedContext.save()\n return nil\n } catch let error as NSError {\n return error\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这是获取函数:
\n\nfunc fetchShoppingItems(completion: @escaping (Set<ShoppingItem>?, Error?) -> Void) {\n let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)\n let viewContext = self.persistentContainer.viewContext\n backgroundContext.parent = viewContext\n backgroundContext.performAndWait {\n let fetchRequest: NSFetchRequest<CDShoppingItem> = CDShoppingItem.fetchRequest()\n do {\n let cdShoppingItems: [CDShoppingItem] = try backgroundContext.fetch(fetchRequest)\n guard !cdShoppingItems.isEmpty else {\n completion([], nil)\n return\n }\n for nextCdShoppingItem in cdShoppingItems {\n }\n completion(shoppingItems, nil) \n } catch let error as NSError {\n completion(nil, error)\n }\n }\n} \nRun Code Online (Sandbox Code Playgroud)\n\n在正常操作中,代码似乎可以工作。
\n\n问题:
\n\n我还编写了一个单元测试来尝试引发多线程问题。此测试使用并发调度队列:
\n\nlet concurrentReadWriteQueue = DispatchQueue(label: \xe2\x80\x9exxx.test_coreDataMultithreading", attributes: .concurrent) \nRun Code Online (Sandbox Code Playgroud)\n\n计时器定义测试时间。
\n在测试方案中,我设置了参数-com.apple.CoreData.Logging.stderr 1和-com.apple.CoreData.ConcurrencyDebug 1。
\n测试期间,overwriteShoppingItems和fetchShoppingItems被重复插入到队列中,并发执行。
\n这个单元测试执行一些读取和写入,然后停在该行
let itemName = nextCdShoppingItem.name! \nRun Code Online (Sandbox Code Playgroud)\n\n因为nextCdShoppingItem.name是nil,这永远不应该发生,因为我从不存储nil。
在崩溃之前,记录以下内容:
\n\nCoreData: error: API Misuse: Attempt to serialize store access on non-owning coordinator (PSC = 0x600000e6c980, store PSC = 0x0)\nRun Code Online (Sandbox Code Playgroud)\n\n如果我只提取或只写入,则不会记录 CoreData 警告。因此,这似乎肯定是一个多线程问题。\n但是,CoreData.ConcurrencyDebug没有检测到它。
看起来好像在一个线程上执行提取操作期间,另一个线程删除了当前提取的项目,因此其属性被读回为nil。\n但这不应该发生,因为提取和保存是通过 完成的backgroundContext.performAndWait,即串行完成的。\n并且堆栈跟踪显示只有一个线程访问 CoreData:Thread 3 Queue : NSManagedObjectContext 0x600003c8c000 (serial)
我的问题:
\n\n编辑:
\n\n也许这有助于识别问题:当我backgroundContext.delete(object)在 中注释掉时overwriteShoppingItems,不再记录错误,并且不会将任何项目作为 获取nil。
小智 16
好吧,我遇到了同样的错误,但只是在“存档”项目时发生。我花了几个小时才找到问题,因为我只在上传时遇到错误,在资产目录中,我有一些重复的图像,通过删除它们,错误就消失了。
如果其他人遇到此错误并且您的 Coredata 设置正常,请检查您的资产文件夹。
| 归档时间: |
|
| 查看次数: |
4188 次 |
| 最近记录: |