如何在Core Data

时间:2018-03-23 17:03:24

标签: ios swift multithreading core-data

我有一个需要大约一分钟设置的应用程序,所以当用户点击“开始新游戏”时,我想在数据加载到Core Data时显示活动微调器。

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

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()中,我需要保存多次,但是当我尝试这样做时它会崩溃。我现在明白了,因为我试图从后台线程访问主要上下文。这是函数的基本结构:

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()
}

根据我的阅读,我认为我需要为后台线程创建另一个托管对象上下文,但我不知道如何解决这个问题以及如何将其纳入我当前的代码中。在进入下一个屏幕之前,我也不知道如何确保数据保存正确。我没有多线程的经验,所以任何帮助都会受到赞赏。

编辑:关于标记为重复,建议的主题为8年,不包含任何答案。我希望在Swift中提供一个简短的代码示例,以获得更新的答案。

1 个答案:

答案 0 :(得分:1)

我在阅读了@ user1046037的有用评论(谢谢!)并研究如何使用context.perform { }context.performAndWait { }后解决了这个问题。我会在下面发布我的代码,以防其他人受益,因为我在swift上找不到任何关于SO的例子:

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类中:

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

相关问题