如何在Core Data中的后台线程中保存到托管对象上下文 [英] How to save to managed object context in a background thread in Core Data

查看:97
本文介绍了如何在Core Data中的后台线程中保存到托管对象上下文的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个大约需要一分钟设置的应用程序,因此,当用户点击开始新游戏"时,我想在数据加载到核心数据中时显示活动微调框.

I have an app which takes about a minute to set up, so when the user taps ‘Start New Game’ I want to show an activity spinner while data is loaded into Core Data.

我知道我必须在后台线程上执行此操作,以便可以在主线程上更新UI,但是我不知道如何在后台线程中保存托管上下文.这是我到目前为止的内容:

I understand I have to do this on a background thread so I can update the UI on the main thread, but I don’t know how to save the managed context in the background thread. Here is what I have so far:

func startNewGame() {
  initiateProgressIndicator() // start the spinner and 'please wait' message

  DispatchQueue.global(qos: .background).async {
    self.coreDataStack.importDefaultData() // set up the database for the new game

    DispatchQueue.main.async {
        stopProgressIndicator()

        // Transition to the next screen
        let vc: IntroViewController = self.storyboard?.instantiateViewController(withIdentifier: "IntroScreen") as! IntroViewController
        vc.rootVCReference = self
        vc.coreDataStack = self.coreDataStack
        self.present(vc, animated:true, completion:nil)
    }
}

在importDefaultData()中,我需要保存多次,但是在尝试保存时会崩溃.我现在知道这是因为我正在尝试从后台线程访问主上下文.这是该函数的基本结构:

In importDefaultData() I need to save multiple times but it crashes when I try to do so. I understand now it's because I'm trying to access the main context from a background thread. Here is the basic structure of the function:

func importDefaultData() {
    // import data into Core Data here, code not shown for brevity

    saveContext()

    // import more data into Core Data here, code not shown for brevity

    saveContext()

    // import final data here

    saveContext()
}

从我所读的内容中,我认为我需要为后台线程创建另一个托管对象上下文,但是我不清楚如何进行此操作以及如何使其适合我的当前代码.我还不知道如何在进入下一个屏幕之前确保正确保存数据.我没有使用多线程的经验,因此将不胜感激.

From what I have read I think I need to create another managed object context for the background thread, but I am unclear on how to go about this and how to fit it into my current code. I also do not know how to make sure the data is saved correctly before I move on to the next screen. I have no experience with multithreading so any help would be appreciated.

关于被标记为重复的主题,建议的主题为8岁,不包含任何答案.我希望可以通过Swift中的简短代码示例获得最新的答案.

regarding being marked as duplicate, the suggested topic is 8 years old and doesn't contain any answers. I was hoping for a more up to date answer with perhaps a brief code example in Swift.

推荐答案

阅读@ user1046037的有用评论(谢谢!)并研究如何使用context.perform { }context.performAndWait { }之后,我已经解决了这个问题.我将在下面发布我的代码,以防其他人受益,因为我无法迅速找到SO上的任何示例:

I have solved this after reading the helpful comments from @user1046037 (thanks!) and researching how to use context.perform { } and context.performAndWait { }. I will post my code below in case it benefits anyone else, as I couldn't find any examples on SO in swift:

initiateProgressIndicator() // start the spinner and 'please wait' message

let childContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
childContext.parent = coreDataStack.managedContext

// Create a background task
childContext.perform {
  // Perform tasks in a background queue
  self.coreDataStack.importDefaultData(childContext: childContext) // set up the database for the new game

  do {
    // Saves the tasks done in the background to the child context
    try childContext.save()

    // Performs a task in the main queue and wait until this tasks finishes
    self.coreDataStack.managedContext.performAndWait {
        do {
            // Saves the data from the child to the main context
            try self.coreDataStack.managedContext.save()

            self.activitySpinner.stopAnimating()

            // Transition to the next screen
            let vc: IntroViewController = self.storyboard?.instantiateViewController(withIdentifier: "IntroScreen") as! IntroViewController
            vc.rootVCReference = self
            vc.coreDataStack = self.coreDataStack
            self.present(vc, animated:true, completion:nil)

        } catch {
            fatalError("Failure to save context: \(error)")
        }
    }
  } catch {
    fatalError("Failure to save context: \(error)")
  }
}

在我的CoreDataStack类中:

In my CoreDataStack class:

func importDefaultData(childContext: NSManagedObjectContext) {
  // import data into Core Data here, code not shown for brevity

  saveChildContext(childContext: childContext)

  // import more data into Core Data here, code not shown for brevity

  saveChildContext(childContext: childContext)

  // import final data here

  saveChildContext(childContext: childContext)
}

func saveChildContext(childContext: NSManagedObjectContext) {
    guard childContext.hasChanges else {
        return
    }

    do {
        try childContext.save()
    } catch {
        let nserror = error as NSError
    }
}

如果这种方法不是最佳做法,或者任何人都可以看到改进的方法,我将不胜感激.我应该补充一点,我发现以下链接非常有用: https://marcosantadev.com/coredata_crud_concurrency_swift_1

If this approach is not best practise or anyone can see a way to improve it, I'd appreciate your thoughts. I should add that I found the following link very helpful: https://marcosantadev.com/coredata_crud_concurrency_swift_1

这篇关于如何在Core Data中的后台线程中保存到托管对象上下文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆