iOS: 同步访问 CoreData
iOS: Synchronizing access to CoreData
我是 CoreData 的新手,我正在尝试创建一个简单的应用程序。
假设我有一个函数:
func saveEntry(entry: Entry) {
let moc = NSManagedObjectContext(concurrencyType: .NSPrivateQueueConcurrencyType)
moc.parentContext = savingContext
moc.pefrormBlockAndWait {
// find if MOC has entry
// if not => create
// else => update
// saving logic here
}
}
它会引入一个问题:如果我从两个线程调用 saveEntry
,传递相同的条目,它将重复它。所以我已经将串行队列添加到我的数据库适配器并按以下方式进行:
func saveEntry(entry: Entry) {
dispatch_sync(serialDbQueue) { // (1)
let moc = NSManagedObjectContext(concurrencyType: .NSPrivateQueueConcurrencyType)
moc.parentContext = savingContext
moc.pefrormBlockAndWait { // (2)
// find if MOC has entry
// if not => create
// else => update
// saving logic here
}
}
}
它工作正常,直到我想添加另一个接口函数:
func saveEntries(entries: [Entry]) {
dispatch_sync(serialDbQueue) { // (3)
let moc = NSManagedObjectContext(concurrencyType: .NSPrivateQueueConcurrencyType)
moc.parentContext = savingContext
moc.pefrormBlockAndWait {
entries.forEach { saveEntry([=13=]) }
}
}
}
现在我遇到了死锁:1 将在 serialDbQueue 上调用并等待保存完成。 2 将在专用队列上调用并等待 3。3 正在等待 1。
那么处理同步访问的正确方法是什么?据我所知,由于此处描述的原因,保留一个 MOC 并对其执行保存是不安全的:http://saulmora.com/coredata/magicalrecord/2013/09/15/why-contextforcurrentthread-doesn-t-work-in-magicalrecord.html
我会尝试用一个 NSManagedObjectContext
作为控制机制来实现它。每个上下文都维护一个串行操作队列,因此多个线程可以调用 performBlock:
或 performBlockAndWait:
而没有任何并发访问的危险(尽管您必须注意上下文数据在块入队时间和块入队时间之间发生变化最终执行)。只要上下文中的所有工作都在正确的队列上完成(通过 performBlock
),将多个线程的工作排队就没有内在的危险。
当然需要考虑一些复杂的因素,如果不了解您的应用程序,我无法提供真正的建议。
- 哪个对象将负责创建此上下文以及如何将它提供给需要它的每个对象?
- 使用共享上下文,很难知道在该上下文上工作的时间 "finished"(它的操作队列为空)是否代表您应用中的有意义状态。
- 使用共享上下文,如果您想在发生错误时放弃未保存的修改,则放弃更改会更加困难(您需要实际还原这些更改,而不是简单地丢弃上下文而不保存)。
我是 CoreData 的新手,我正在尝试创建一个简单的应用程序。
假设我有一个函数:
func saveEntry(entry: Entry) {
let moc = NSManagedObjectContext(concurrencyType: .NSPrivateQueueConcurrencyType)
moc.parentContext = savingContext
moc.pefrormBlockAndWait {
// find if MOC has entry
// if not => create
// else => update
// saving logic here
}
}
它会引入一个问题:如果我从两个线程调用 saveEntry
,传递相同的条目,它将重复它。所以我已经将串行队列添加到我的数据库适配器并按以下方式进行:
func saveEntry(entry: Entry) {
dispatch_sync(serialDbQueue) { // (1)
let moc = NSManagedObjectContext(concurrencyType: .NSPrivateQueueConcurrencyType)
moc.parentContext = savingContext
moc.pefrormBlockAndWait { // (2)
// find if MOC has entry
// if not => create
// else => update
// saving logic here
}
}
}
它工作正常,直到我想添加另一个接口函数:
func saveEntries(entries: [Entry]) {
dispatch_sync(serialDbQueue) { // (3)
let moc = NSManagedObjectContext(concurrencyType: .NSPrivateQueueConcurrencyType)
moc.parentContext = savingContext
moc.pefrormBlockAndWait {
entries.forEach { saveEntry([=13=]) }
}
}
}
现在我遇到了死锁:1 将在 serialDbQueue 上调用并等待保存完成。 2 将在专用队列上调用并等待 3。3 正在等待 1。
那么处理同步访问的正确方法是什么?据我所知,由于此处描述的原因,保留一个 MOC 并对其执行保存是不安全的:http://saulmora.com/coredata/magicalrecord/2013/09/15/why-contextforcurrentthread-doesn-t-work-in-magicalrecord.html
我会尝试用一个 NSManagedObjectContext
作为控制机制来实现它。每个上下文都维护一个串行操作队列,因此多个线程可以调用 performBlock:
或 performBlockAndWait:
而没有任何并发访问的危险(尽管您必须注意上下文数据在块入队时间和块入队时间之间发生变化最终执行)。只要上下文中的所有工作都在正确的队列上完成(通过 performBlock
),将多个线程的工作排队就没有内在的危险。
当然需要考虑一些复杂的因素,如果不了解您的应用程序,我无法提供真正的建议。
- 哪个对象将负责创建此上下文以及如何将它提供给需要它的每个对象?
- 使用共享上下文,很难知道在该上下文上工作的时间 "finished"(它的操作队列为空)是否代表您应用中的有意义状态。
- 使用共享上下文,如果您想在发生错误时放弃未保存的修改,则放弃更改会更加困难(您需要实际还原这些更改,而不是简单地丢弃上下文而不保存)。