Ios CoreData错误:API误用:尝试序列化非所有者协调器上的存储访问

Ios CoreData错误:API误用:尝试序列化非所有者协调器上的存储访问,ios,multithreading,core-data,Ios,Multithreading,Core Data,注: 不适用,因为我实际使用CoreData。 在最后一个答案中,建议在添加新对象之前获取新后台线程中的所有项目,但这是在我的代码中完成的。 建议在保存项目的上下文之前取消其结果,但这也在我的代码中完成 我的应用程序使用CoreData存储名为shoppingItems的对象。我编写了一个类CoreDataManager,它初始化CoreData,基本上有一个函数覆盖当前存储的项,还有一个函数获取所有项。这两个函数都在后台运行,即在单独的线程上运行 这是我的代码(不相关的部分被省略) 我在主线程

注:
不适用,因为我实际使用CoreData。
在最后一个答案中,建议在添加新对象之前获取新后台线程中的所有项目,但这是在我的代码中完成的。
建议在保存项目的上下文之前取消其结果,但这也在我的代码中完成

我的应用程序使用CoreData存储名为
shoppingItems
的对象。我编写了一个类
CoreDataManager
,它初始化CoreData,基本上有一个函数覆盖当前存储的项,还有一个函数获取所有项。这两个函数都在后台运行,即在单独的线程上运行

这是我的代码(不相关的部分被省略)

我在主线程上设置核心数据:

private lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: modelName)
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
    })
    return container
}()  
这是写函数:

func overwriteShoppingItems(_ shoppingItems: Set<ShoppingItem>, completion: @escaping (Error?) -> Void) {
        let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
        let viewContext = self.persistentContainer.viewContext
        backgroundContext.parent = viewContext
        backgroundContext.performAndWait {
            // Delete currently stored shopping items
            let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: CDEntityShoppingItem)
            do {
                let result = try backgroundContext.fetch(fetchRequest)
                let resultData = result as! [NSManagedObject]
                for object in resultData {
                    backgroundContext.delete(object)
                }

                if !shoppingItems.isEmpty {
                    // Save shopping items in managed context
                    let cdShoppingItemEntity = NSEntityDescription.entity(forEntityName: CDEntityShoppingItem, in: backgroundContext)!
                    for nextShoppingItem in shoppingItems {
                        let nextCdShoppingItem = CDShoppingItem(entity: cdShoppingItemEntity, insertInto: backgroundContext)
                        nextCdShoppingItem.name = nextShoppingItem.name
                    }
                }

                let saveError = self.saveManagedContext(managedContext: backgroundContext)
                completion(saveError)
            } catch let error as NSError {
                completion(error)
            }
        }
}

func saveManagedContext(managedContext: NSManagedObjectContext) -> Error? {
    if !managedContext.hasChanges { return nil }
    do {
        try managedContext.save()
        return nil
    } catch let error as NSError {
        return error
    }
}
func fetchShoppingItems(completion: @escaping (Set<ShoppingItem>?, Error?) -> Void) {
        let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
        let viewContext = self.persistentContainer.viewContext
        backgroundContext.parent = viewContext
        backgroundContext.performAndWait {
            let fetchRequest: NSFetchRequest<CDShoppingItem> = CDShoppingItem.fetchRequest()
            do {
                let cdShoppingItems: [CDShoppingItem] = try backgroundContext.fetch(fetchRequest)
                guard !cdShoppingItems.isEmpty else {
                    completion([], nil)
                    return
                }
                for nextCdShoppingItem in cdShoppingItems {
                }
                    completion(shoppingItems, nil) 
            } catch let error as NSError {
                completion(nil, error)
            }
        }
}  
计时器定义测试时间。
在测试方案中,我设置了参数
-com.apple.CoreData.Logging.stderr 1
-com.apple.CoreData.concurrenceydebug 1

在测试期间,
overwriteShoppingItems
fetchShoppingItems
被重复插入队列,并同时执行。
此单元测试在停在第行之前执行一些读写操作

                    let itemName = nextCdShoppingItem.name!  
因为
nextCdShoppingItem.name
nil
,这不应该发生,因为我从不存储
nil

在碰撞发生之前,将记录以下内容:

CoreData: error:  API Misuse: Attempt to serialize store access on non-owning coordinator (PSC = 0x600000e6c980, store PSC = 0x0)
如果我只进行读取或写入,CoreData警告将记录。因此,这显然是一个多线程问题。 但是,
CoreData.concurrenceydebug
没有检测到它

看起来好像在一个线程的获取操作期间,另一个线程删除了当前获取的项,以便将其属性读回为
nil
。 但这种情况不应该发生,因为回迁和保存是通过
backgroundContext.performAndWait
完成的,即串行进行的。 堆栈跟踪显示只有一个线程访问CoreData:
线程3队列:NSManagedObjectContext 0x600003c8c000(串行)

我的问题:

  • 我滥用CoreDataAPI的原因是什么?如何正确使用
  • 为什么一个项目有时被读回为零
编辑:


也许这有助于识别问题:当我在
overwriteShoppingItems
中注释掉
backgroundContext.delete(object)
时,错误不再被记录,并且没有项目被提取为
nil

问题似乎得到了解决。
这显然发生了,因为函数
overwriteShoppingItems
fetchShoppingItems
都使用
let backgroundContext=NSManagedObjectContext(concurrencyType:.privateQueueConcurrencyType)设置了一个单独的后台上下文
使用它们自己的队列,以便获取和保存不会由单个队列序列化

我现在用以下方式修改了代码:
我现在有一处房产

let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)  
初始化为

self.backgroundContext.persistentStoreCoordinator = self.persistentContainer.persistentStoreCoordinator
self.persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
并使用

backgroundContext.perform {…}  

现在CoreData错误不再被记录,并且没有项目被作为nil获取。

在我的例子中,我创建了一个新的
managedObjectContext
,但没有(尚未)分配持久存储。最后,我调用了
-[psc metadataForPersistentStore:store]
,而store为nil。这导致写入日志消息。
(旁注:您可以通过在fprintf上设置断点来捕获这些输出)

如果您在项目中未使用核心数据,请检查资产文件是否正确

我也得到这样的错误,然后我已经找到了这个错误,从我的项目

backgroundContext.perform {…}