首页 iOS.& Swift Books 通过教程并发

11
核心数据 由Scott Grosch撰写

您可以开发的许多IOS应用可能会使用核心数据进行数据存储。虽然混合并发和核心数据在现代版本的iOS中不再复杂,但仍然有几个您想要了解的主要概念。就像大多数uikit一样,核心数据不是线程安全的。

笔记:本章假定您已经知道如何执行基本的核心数据概念,如搜索,创建实体等......请查看我们的书, 通过教程的核心数据//bit.ly/2VkUKNb 如果您是核心数据的新功能。

nsmanagedObjectContext不是线程安全的

The NSManagedObjectContext, which gets created as part of an NSPersistentContainer from your AppDelegate, is tied to the main thread. As you’ve learned throughout the rest of this book, this means you can only use that context on the main UI thread. However, if you do so, you’re going to negatively impact your user’s experience.

There are two methods available on the NSManagedObjectContext class to help with concurrency:

  • perform(_:)
  • performAndWait(_:)

两种方法所做的是确保在创建上下文的同一队列中执行您传递给闭包的任何操作。请注意,在主线程上是如何“不”的。“您可以在另一个队列上创建自己的上下文。因此,这些方法确保您始终针对正确的线程执行,并且不会在运行时崩溃。

两者之间的唯一区别是第一个是异步方法,而第二则是同步的。在不利用这些方法中的任何一种,您应该异常执行核心数据任务。即使您可能知道您已经在适当的线程上,您也可以在将来重构代码,并忘记在该时间点包裹核心数据呼叫。拯救自己头痛并从一开始就使用它们!

导入数据

When your app starts, one of the first goals it frequently has is to contact the server and download any new data. Once the network operation completes, an expensive compare and import cycle will have to take place. However, you don’t need to create an entire Operation for this common task. Core Data’s NSPersistentContainer provides performBackgroundTask(_:) which will help you out:

persistentContainer.performBackgroundTask { context in
  for json in jsonDataFromServer {
    let obj = MyEntity(context: context)
    obj.populate(from: json)
  }
  
  do {
    try context.save()
  } catch {
    fatalError("Failed to save context")
  }
}
let childContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
childContext.parent = persistentContainer.viewContext

childContext.perform { 
  ...
}

nsasynchronousfetchrequest.

When you use an NSFetchRequest to query Core Data, the operation is synchronous. If you’re just grabbing a single object, that’s perfectly acceptable. When you’re performing a time-consuming query, such as retrieving data to populate a UITableView, then you’ll prefer to perform the query asynchronously. Using nsasynchronousfetchrequest. is the obvious solution.

let ageKeyPath = #keyPath(Person.age)

let fetchRequest = Person.fetchRequest() as NSFetchRequest<Person>
fetchRequest.predicate = NSPredicate(format: "%K > 13", ageKeyPath)

let asyncFetch = nsasynchronousfetchrequest.(fetchRequest: fetchRequest) {
  [weak self] result in

  guard let self = self,
        let people = result.finalResult else {
    return
  }

  self.tableData = people

  DispatchQueue.main.async {
	  self.tableView.reloadData()
  }
}

do {
  let backgroundContext = persistentContainer.newBackgroundContext()
  try backgroundContext.execute(asyncFetch)
} catch let error {
  // handle error
}

共享NSManagedObject.

You can’t share an NSManagedObject — or a subclass — between threads. Sure, you 能够,往往不会是它的 似乎 工作,但你的应用程序将打破,所以不要这样做!

let objectId = someEntity.objectID

DispatchQueue.main.async { [weak self] in
  guard let self = self else { return }
  
  let myEntity = self.managedObjectContext.object(with: objectId)
  self.addressLabel.text = myEntity.address
}

使用concurrencydebug.

To help protect yourself from sharing an NSManagedObject across threads, you can enable a runtime debug flag by editing the project’ scheme and passing a runtime argument.

private let passActualObject = true
private let passActualObject = false

然后去哪儿?

希望,前几页已经表明您以线程安全的方式使用核心数据非常容易。如果您之前从这个伟大的框架中避开了,因为它很难同时使用,现在是时候拍另一次外观!

有一个技术问题?想报告一个错误吗? 您可以向官方书籍论坛中的书籍作者提出问题和报告错误 这里.

有反馈分享在线阅读体验吗? 如果您有关于UI,UX,突出显示或我们在线阅读器的其他功能的反馈,您可以将其发送到设计团队,其中表格如下所示:

© 2021 Razeware LLC

您可以免费读取,本章的部分显示为 混淆了 文本。解锁这本书,以及我们整个书籍和视频目录,带有Raywenderlich.com的专业订阅。

现在解锁

要突出或记笔记,您需要在订阅中拥有这本书或自行购买。