当保存在后台异步完成时,我应该如何保证嵌套上下文中不同线程的获取结果是最新的?
How should I guarantee fetch results from a different thread in a nested contexts are up to date, when saves are done asynchronously in background?
我已阅读以下内容Behavior differences between performBlock: and performBlockAndWait:?
但无法找到我的问题的答案。
以下代码摘自 RayWenderlich video. Specifically at 10:05 代码是 某事 像这样:
class CoreDataStack {
var coordinator : NSPersistentStoreCoordinator
init(coordinator: NSPersistentStoreCoordinator){
self.coordinator = coordinator
}
// private, parent, in background used for saving
private lazy var savingContext : NSManagedObjectContext = {
let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
moc.persistentStoreCoordinator = coordinator
return moc
}()
lazy var mainManagedObjectedContext : NSManagedObjectContext = {
let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
moc.parent = self.savingContext
return moc
}()
func saveMainContext() {
guard savingContext.hasChanges || mainManagedObjectedContext.hasChanges else {
return
}
mainManagedObjectedContext.performAndWait {
do {
try mainManagedObjectedContext.save()
}catch let error{
fatalError(error.localizedDescription)
}
}
savingContext.perform {
do {
try self.savingContext.save()
}catch let error{
fatalError(error.localizedDescription)
}
}
}
}
据我了解,主上下文只是将更改传递给它的父上下文,它是一个私有的后台上下文。它同步执行此操作。
然后父级私有上下文在后台线程异步 中针对 sqlite 进行实际保存。长话短说,这对我们的性能有很大帮助。但是数据完整性呢?!
想象一下,如果我这样做:
let coredataManager = CoreDataStack()
coredataManager.saveMainContext() // save is done asynchronously in background queue
coredataManager.mainManagedObjectedContext.fetch(fetchrequest)
如何保证我的 fetch 正在读取最新和更新的结果?
如果我们异步进行写入,那么是否有可能同时进行另一次读取会导致意外结果,即保存更改的结果可能存在也可能不存在?
编辑:
我对下面的代码进行了改进。我可以让我的保存接受一个 completionHandler 参数。但这并不能解决整个问题。如果我正在从其他地方的 mainQueue 发出 fetchRequest 而不知道同时正在发生保存怎么办?
enum SaveStatus{
case noChanges
case failure
case success
}
func saveMainContext(completionHandler: (SaveStatus -> ())) {
guard savingContext.hasChanges || mainManagedObjectedContext.hasChanges else {
completionHandler(.noChanges)
return
}
mainManagedObjectedContext.performAndWait {
do {
try mainManagedObjectedContext.save()
}catch let error{
completionHandler(.failure)
fatalError(error.localizedDescription)
}
}
savingContext.perform {
do {
try self.savingContext.save()
completionHandler(.succes)
}catch let error{
completionHandler(.failure)
fatalError(error.localizedDescription)
}
}
}
所有对 mainManagedObjectContext
的调用都是同步的,因此是阻塞的。如果您调用 saveMainContext()
并在之后立即调用 mainManagedObjectedContext.fetch(fetchrequest)
,即使 save/fetch 请求来自不同的队列(请参阅上面的 link 中的 FIFO)。
当您执行提取请求时,您并不是从持久存储中提取 - 您是从您刚刚更新的子容器中提取。您不需要等待更改提交到持久存储,因为您不是从那里访问数据。子容器会给你最新的变化。
子容器 是 一个容器 - 它将在内存中保存您的最新更改(而不是存储在磁盘上 - 这是持久性容器的工作)。
这里真正的问题是你的 CoreDataStack
应该实现单例模式以防止实例化相同容器的多个版本(从技术上讲,它仍然在同一个线程上并因此被序列化,但访问容器会' 是线程安全的)。换句话说,每次实例化 CoreDataStack()
时,都会创建一个新的 savingContext
和 mainManagedObjectedContext
.
相反,只实例化它一次。
class CoreDataStack {
var coordinator: NSPersistentStoreCoordinator
public static let sharedInstance = CoreDataStack()
private override init() {
self.coordinator = NSPersistantStoreCoordinator()
}
...
rest of your code here
...
}
然后这样调用:
CoreDataStack.sharedInstance.saveMainContext()
(参见 回复:'does the child have the same objects as the parent?')
子项与父项不同步的唯一情况是您有多个子项访问同一个父项 - 但这里似乎不是这种情况。
这个问题并不特定于核心数据。
这是一道经典的读写题。
保护数据源的常用方法是使用串行队列访问数据源。否则,如果没有串行队列,您将遇到读写问题。
在下面的例子中:
let coredataManager = CoreDataStack() // 1
coredataManager.saveMainContext() // 2 save is done asynchronously in background queue
coredataManager.mainManagedObjectedContext.fetch(fetchrequest) // 3
coredataManager
将从串行队列中访问。因此,即使第 2 行的写入是异步完成的,第 3 行的读取也必须等到 serial 队列解除阻塞。
我已阅读以下内容Behavior differences between performBlock: and performBlockAndWait:? 但无法找到我的问题的答案。
以下代码摘自 RayWenderlich video. Specifically at 10:05 代码是 某事 像这样:
class CoreDataStack {
var coordinator : NSPersistentStoreCoordinator
init(coordinator: NSPersistentStoreCoordinator){
self.coordinator = coordinator
}
// private, parent, in background used for saving
private lazy var savingContext : NSManagedObjectContext = {
let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
moc.persistentStoreCoordinator = coordinator
return moc
}()
lazy var mainManagedObjectedContext : NSManagedObjectContext = {
let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
moc.parent = self.savingContext
return moc
}()
func saveMainContext() {
guard savingContext.hasChanges || mainManagedObjectedContext.hasChanges else {
return
}
mainManagedObjectedContext.performAndWait {
do {
try mainManagedObjectedContext.save()
}catch let error{
fatalError(error.localizedDescription)
}
}
savingContext.perform {
do {
try self.savingContext.save()
}catch let error{
fatalError(error.localizedDescription)
}
}
}
}
据我了解,主上下文只是将更改传递给它的父上下文,它是一个私有的后台上下文。它同步执行此操作。
然后父级私有上下文在后台线程异步 中针对 sqlite 进行实际保存。长话短说,这对我们的性能有很大帮助。但是数据完整性呢?!
想象一下,如果我这样做:
let coredataManager = CoreDataStack()
coredataManager.saveMainContext() // save is done asynchronously in background queue
coredataManager.mainManagedObjectedContext.fetch(fetchrequest)
如何保证我的 fetch 正在读取最新和更新的结果?
如果我们异步进行写入,那么是否有可能同时进行另一次读取会导致意外结果,即保存更改的结果可能存在也可能不存在?
编辑: 我对下面的代码进行了改进。我可以让我的保存接受一个 completionHandler 参数。但这并不能解决整个问题。如果我正在从其他地方的 mainQueue 发出 fetchRequest 而不知道同时正在发生保存怎么办?
enum SaveStatus{
case noChanges
case failure
case success
}
func saveMainContext(completionHandler: (SaveStatus -> ())) {
guard savingContext.hasChanges || mainManagedObjectedContext.hasChanges else {
completionHandler(.noChanges)
return
}
mainManagedObjectedContext.performAndWait {
do {
try mainManagedObjectedContext.save()
}catch let error{
completionHandler(.failure)
fatalError(error.localizedDescription)
}
}
savingContext.perform {
do {
try self.savingContext.save()
completionHandler(.succes)
}catch let error{
completionHandler(.failure)
fatalError(error.localizedDescription)
}
}
}
所有对 mainManagedObjectContext
的调用都是同步的,因此是阻塞的。如果您调用 saveMainContext()
并在之后立即调用 mainManagedObjectedContext.fetch(fetchrequest)
,即使 save/fetch 请求来自不同的队列(请参阅上面的 link 中的 FIFO)。
当您执行提取请求时,您并不是从持久存储中提取 - 您是从您刚刚更新的子容器中提取。您不需要等待更改提交到持久存储,因为您不是从那里访问数据。子容器会给你最新的变化。
子容器 是 一个容器 - 它将在内存中保存您的最新更改(而不是存储在磁盘上 - 这是持久性容器的工作)。
这里真正的问题是你的 CoreDataStack
应该实现单例模式以防止实例化相同容器的多个版本(从技术上讲,它仍然在同一个线程上并因此被序列化,但访问容器会' 是线程安全的)。换句话说,每次实例化 CoreDataStack()
时,都会创建一个新的 savingContext
和 mainManagedObjectedContext
.
相反,只实例化它一次。
class CoreDataStack {
var coordinator: NSPersistentStoreCoordinator
public static let sharedInstance = CoreDataStack()
private override init() {
self.coordinator = NSPersistantStoreCoordinator()
}
...
rest of your code here
...
}
然后这样调用:
CoreDataStack.sharedInstance.saveMainContext()
(参见
子项与父项不同步的唯一情况是您有多个子项访问同一个父项 - 但这里似乎不是这种情况。
这个问题并不特定于核心数据。
这是一道经典的读写题。
保护数据源的常用方法是使用串行队列访问数据源。否则,如果没有串行队列,您将遇到读写问题。
在下面的例子中:
let coredataManager = CoreDataStack() // 1
coredataManager.saveMainContext() // 2 save is done asynchronously in background queue
coredataManager.mainManagedObjectedContext.fetch(fetchrequest) // 3
coredataManager
将从串行队列中访问。因此,即使第 2 行的写入是异步完成的,第 3 行的读取也必须等到 serial 队列解除阻塞。