首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >CoreData错误: API滥用:尝试序列化非所有者协调器上的存储访问

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

Stack Overflow用户
提问于 2019-06-12 10:26:02
回答 5查看 2K关注 0票数 2

注意:

这个职位不适用,因为我实际上使用CoreData。

这个职位中,最后一个答案建议在添加新对象之前获取新后台线程中的所有项,但这是在我的代码中完成的。

这个职位建议在保存一个项的上下文之前取消它的错误,但这也是在我的代码中完成的。

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

这是我的代码(不相关的部分被遗漏了)。

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

代码语言:javascript
复制
private lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: modelName)
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
    })
    return container
}()  

这是写函数:

代码语言:javascript
复制
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
    }
}

这是提取函数:

代码语言:javascript
复制
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)
            }
        }
}  

在正常操作中,代码似乎有效。

问题:

我还编写了一个单元测试,试图引发多线程问题。此测试使用并发分派队列:

代码语言:javascript
复制
let concurrentReadWriteQueue = DispatchQueue(label: „xxx.test_coreDataMultithreading", attributes: .concurrent)  

计时器定义测试时间。

在测试方案中,我设置了参数-com.apple.CoreData.Logging.stderr 1-com.apple.CoreData.ConcurrencyDebug 1

在测试期间,overwriteShoppingItemsfetchShoppingItems被反复插入到队列中,并并发执行。

此单元测试在行停止之前执行一些读和写操作。

代码语言:javascript
复制
                    let itemName = nextCdShoppingItem.name!  

因为nextCdShoppingItem.namenil,这不应该发生,因为我从不存储nil

在崩溃之前,将记录以下内容:

代码语言:javascript
复制
CoreData: error:  API Misuse: Attempt to serialize store access on non-owning coordinator (PSC = 0x600000e6c980, store PSC = 0x0)

如果我只获取或只写,CoreData警告是而不是日志。因此,这显然是一个多线程问题。然而,CoreData.ConcurrencyDebug没有检测到它。

看起来,在对一个线程进行提取操作期间,另一个线程删除当前获取的项,以便将其属性读入为nil。但是这种情况不应该发生,因为获取和保存都是用backgroundContext.performAndWait完成的,也就是串行的。堆栈跟踪显示,只有一个线程访问CoreData:Thread 3 Queue : NSManagedObjectContext 0x600003c8c000 (serial)

我的问题:

  • 我对CoreData API的误用是什么,以及如何正确处理它?
  • 为什么一项有时会被读回零?

编辑:

这可能有助于识别问题:当我在overwriteShoppingItems中注释掉overwriteShoppingItems时,错误将不再被记录,并且不会以nil的形式获取任何项。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2019-06-13 08:30:06

问题似乎已经解决了。

显然发生了这种情况,因为函数overwriteShoppingItemsfetchShoppingItems都用自己的队列使用let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)设置了一个单独的后台上下文,这样获取和保存就不会被单个队列序列化。

我现在以以下方式修改了我的代码:

我现在有了财产

代码语言:javascript
复制
let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)  

被初始化为

代码语言:javascript
复制
self.backgroundContext.persistentStoreCoordinator = self.persistentContainer.persistentStoreCoordinator
self.persistentContainer.viewContext.automaticallyMergesChangesFromParent = true

而获取和保存则使用

代码语言:javascript
复制
backgroundContext.perform {…}  

现在,CoreData错误不再被记录,并且没有以零的形式获取任何项。

票数 3
EN

Stack Overflow用户

发布于 2020-12-29 10:08:14

如果您没有在项目中使用核心数据,那么检查您的资产文件是否正确。

我也得到了这样的错误,然后我从我的项目中发现了这个错误。

票数 4
EN

Stack Overflow用户

发布于 2021-12-28 22:23:35

好的,我得到了同样的错误,但只有在“归档”项目的时候。我花了几个小时才找到这个问题,因为我只是在上传时才得到错误,在资产目录中,我有一些重复的图像,通过删除它们,错误就消失了。

如果其他人收到此错误,并且您的Coredata设置没有问题,请检查您的“资产”文件夹。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56559944

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档