【问题标题】:How to use Core Data managedObjectContext properly and a proper way to save context?如何正确使用 Core Data managedObjectContext 以及保存上下文的正确方法?
【发布时间】:2018-05-30 09:51:41
【问题描述】:

在我的应用程序中,我有一个特殊的 CoreDataManager 类,它负责我的应用程序中的所有核心数据操作。

CoreDataManager 类中的第一行如下所示:

   static var sharedInstance: CoreDataManager = { // or private static var
        let instance = CoreDataManager()
        return instance
    }()

    // Init
    override init() {
        self.persistentContainer = NSPersistentContainer.init(name: "AppName")
        self.persistentContainer.loadPersistentStores { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("CoreDataManager: init, loadPersistentStores error: \(error), \(error.userInfo)")
            } else {
                print("CoreDataManager: init() func finished with Success!")


            }
        }

        self.persistentContainer.viewContext.undoManager = nil 
        self.persistentContainer.viewContext.shouldDeleteInaccessibleFaults = true
        self.persistentContainer.viewContext.automaticallyMergesChangesFromParent = true

    }

    let persistentContainer: NSPersistentContainer
    var viewContext: NSManagedObjectContext? {
        return self.persistentContainer.viewContext
    }


    func saveViewContext() {

        let context = self.viewContext

        if self.viewContext != nil {

            if context!.hasChanges {
                do {
                    try context!.save()

                } catch {

                    let nserror = error as NSError
                    fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
                }

            }


        }

当我需要在我的应用程序中保存、编辑或删除核心数据实体时,我会调用 CoreDataManager 类的单例,以及在该类中声明的(保存、编辑、删除实体)方法。

在我的应用程序中,我还有一个 LocalNotificationManager,当我收到来自 CloudKit 的更改或删除某些实体时,我会在其中对 CoreData 实体进行一些操作。当我想保存更改时,我打电话给CoreDataManager.sharedInstance.saveViewContext(),并且“认为我做错了,因为有时我会出错。

不清楚如何正确调用context并保存,有以下疑问:

在我的保存、编辑、删除调用中,我应该使用/保存 ma​​inContext 还是 privateContext

收到通知后,我应该使用/保存 ma​​inContext 还是 privateContext

什么时候应该从主线程保存 viewContext,什么时候使用私有托管对象上下文?我应该使用 .MainQueueConcurrencyType 还是可以避免?

我应该在我的 CoreDataManager 类中声明一个 privateObjectContext 变量吗?这样做的正确方法是什么?

我已经看到可以通过这种方式声明私有上下文,但是我可以在我的 CoreDataManager 类中实现它吗?什么时候应该调用它而不是 saveViewContext()

 let moc = NSManagedObjectContext(concurrencyType:.MainQueueConcurrencyType)
 let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
 privateMOC.parentContext = moc
 privateMOC.performBlock({
     do {
         try privateMOC.save()
     } catch {
         fatalError("Failure to save context: \(error)")
     }
     })

谁能解释清楚在不同情况下保存上下文的正确用法?

Swift 4,Xcode 9。

【问题讨论】:

  • privateObjectContext 用于在后台线程上使用上下文,除非您看到性能问题或 UI 锁定您在主线程上使用标准 viewContext 的罚款。

标签: ios swift core-data concurrency nsmanagedobjectcontext


【解决方案1】:

你有两个选择:

简单的方法

只能从主线程读取和写入 viewContext。这将起作用,但如果您一次读取或写入大量数据,它将阻塞您的 UI。如果您的应用程序在 core-data 中存储了少量内容,这将正常工作,并且是迄今为止最简单的选择。

复杂的方式

使用 viewContext 进行所有阅读,但永远不要对其进行写入。只能从主线程访问 viewContext。使用persistentContainer.performBackgroundTask 完成所有写作,并使用传递到块中的上下文。不要在此块内使用任何其他 managedObjects。如果您需要然后传递一个 objectId 并在块内进行提取。不要在这些块之外传递任何物体。如果您发现需要,请使用 objectId。您还需要创建一个 operationQueue 并将所有对 performBackgroundTask 的调用包装在其中,这样您就不会遇到任何写入冲突。更完整的描述可以在这里找到:NSPersistentContainer concurrency for saving to core data

【讨论】:

  • 谢谢,简单的方法,你能解释一下吗:在我的 ViewController (VC) 中,我进行了写入(编辑/删除)(使用 writeFunction())和读取(使用返回的核心数组获取数据对象)调用(使用 readFunction())。在我的 VC 中,我在主线程上运行 writeFunction() 和 readFunction():DispatchQueue.main.async { CoreDataManager.sharedInstance.readFunction() // same for writeFunction() } 我是否应该在主线程上的 writeFunction() 中使用 DispatchQueue.main.async 调用 saveViewContext()?或者仅在 VC 中使用 DispatchQueue.main.async 就足够了?
  • 如果你正在做复杂的事情,你永远不应该保存 viewContext,因为你不应该首先写入它。对于 performBackgroundTask 上下文,您应该将它们保存在传递的块中 - 不要将其分派给任何其他线程。
  • 我想我会走“简单的方法”乔恩。因此,在上面的问题中,我询问了“一种简单的方法”。对于您之前描述的“一种简单方法”,从 VC 和 writeFunction() 内部进行 DispatchQueue.main.async { CoreDataManager.sharedInstance.writeFunction() } 调用是否是一种正确的技术,我应该调用 CoreDataManager.sharedInstance.saveViewContext() 还是应该是 DispatchQueue.main .async { CoreDataManager.sharedInstance.saveViewContext() }?
  • 如果你从 viewController 调用你通常已经在主线程上,调用DispatchQueue.main.async 是不必要的,但是,除了延迟执行之外,它不会造成太大的伤害。如果总是从主线程调用CoreDataManager.sharedInstance.writeFunction,那么调用DispatchQueue.main.async 也是不必要的,但同样,除了使执行变慢之外不会造成太大的伤害。如果您处于您不知道的状态,您可以使用Thread.isMainThread 测试您是否在主线程上。
  • 它是只读的,你不能分配一个不同的上下文作为 viewContext,而不是你不能在它上面调用 viewContext.save()。
猜你喜欢
  • 2022-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-13
  • 2015-05-04
  • 2012-05-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多