用于核心数据操作的 NSOperation
NSOperation for Core Data operations
我的应用程序进行了大量从某种格式到 NSManagedObject
的大量转换,所以对我来说最好的方法是使用 NSOperation
(或 Operation
in Swift 3 ) 将原始数据转换为 NSManagedObject
,并在所有操作完成后保存该上下文。
我不能为每个 Operation
使用单独的上下文,因为我的转换器生成关系(并且它们只能从相同的上下文访问)并且应用程序可能 运行 最多 20 次转换,所以在每次转换后创建新的上下文并保存它并不酷。
所以我需要创建单独的 OperationQueue
并确保其中的所有操作都是从与上下文相同的线程执行的,我不知道该怎么做。
我只有一个想法:将 Operation.main()
中的所有内容都作为 context.perform { }
启动,但我真的不认为这是一个好的解决方案。
我找到了 similar thread at Whosebug,但答案已经过时,我发现接受的答案显然不正确。
使用您自己的操作队列不是最好的方法。对于 Core Data,您需要通过 mainQueueConcurrencyType
或 privateQueueConcurrencyType
使用内置的 Core Data 并发,然后使用 perform
或 performAndWait
。使用您描述的自定义操作队列将无法正常工作。
这将使用由 Core Data 管理的调度队列。您无法直接计算未决操作的数量,但您可以一次添加一堆块,最后一个块在导入过程完成时执行任何需要发生的事情。在导入过程中保存而不是仅在最后保存也是一个好主意,以防止内存使用失控,除非您只导入少量数据。
我发现在 Operation
中使用核心数据的最佳方法是使用 privateQueueConcurrencyType
创建子上下文,而无需任何额外的 .perform
块(私有操作已经在需要线程)。我愿意接受任何其他建议。
我已经使用 operationQueue.maxConcurrentOperationCount = 1
来确保安全和没有合并冲突,但我可能会建议该方法适用于并发操作,但在大多数情况下它是无用的,因为操作会相互等待上下文未合并。
在与 parentContext
相同的线程中使用 waitUntilAllOperationsAreFinished()
时要小心,在大多数情况下会导致死锁。
示例代码
class ExampleOperation: Operation {
let parentContext: NSManagedObjectContext
init(parentContext: NSManagedObjectContext) {
self.parentContext = parentContext
super.init()
}
override func main() {
if self.isCancelled { return }
let childContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
childContext.parent = parentContext
// use here `childContext` context directly
// e.g.: let result = try childContext.fetch(fetchRequest)
try? childContext.save()
}
我的应用程序进行了大量从某种格式到 NSManagedObject
的大量转换,所以对我来说最好的方法是使用 NSOperation
(或 Operation
in Swift 3 ) 将原始数据转换为 NSManagedObject
,并在所有操作完成后保存该上下文。
我不能为每个 Operation
使用单独的上下文,因为我的转换器生成关系(并且它们只能从相同的上下文访问)并且应用程序可能 运行 最多 20 次转换,所以在每次转换后创建新的上下文并保存它并不酷。
所以我需要创建单独的 OperationQueue
并确保其中的所有操作都是从与上下文相同的线程执行的,我不知道该怎么做。
我只有一个想法:将 Operation.main()
中的所有内容都作为 context.perform { }
启动,但我真的不认为这是一个好的解决方案。
我找到了 similar thread at Whosebug,但答案已经过时,我发现接受的答案显然不正确。
使用您自己的操作队列不是最好的方法。对于 Core Data,您需要通过 mainQueueConcurrencyType
或 privateQueueConcurrencyType
使用内置的 Core Data 并发,然后使用 perform
或 performAndWait
。使用您描述的自定义操作队列将无法正常工作。
这将使用由 Core Data 管理的调度队列。您无法直接计算未决操作的数量,但您可以一次添加一堆块,最后一个块在导入过程完成时执行任何需要发生的事情。在导入过程中保存而不是仅在最后保存也是一个好主意,以防止内存使用失控,除非您只导入少量数据。
我发现在 Operation
中使用核心数据的最佳方法是使用 privateQueueConcurrencyType
创建子上下文,而无需任何额外的 .perform
块(私有操作已经在需要线程)。我愿意接受任何其他建议。
我已经使用 operationQueue.maxConcurrentOperationCount = 1
来确保安全和没有合并冲突,但我可能会建议该方法适用于并发操作,但在大多数情况下它是无用的,因为操作会相互等待上下文未合并。
在与 parentContext
相同的线程中使用 waitUntilAllOperationsAreFinished()
时要小心,在大多数情况下会导致死锁。
示例代码
class ExampleOperation: Operation {
let parentContext: NSManagedObjectContext
init(parentContext: NSManagedObjectContext) {
self.parentContext = parentContext
super.init()
}
override func main() {
if self.isCancelled { return }
let childContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
childContext.parent = parentContext
// use here `childContext` context directly
// e.g.: let result = try childContext.fetch(fetchRequest)
try? childContext.save()
}