【问题标题】:NSOperation for Core Data operations用于核心数据操作的 NSOperation
【发布时间】:2017-02-07 19:04:02
【问题描述】:

我的应用程序进行了大量从某种格式到NSManagedObject 的转换,所以对我来说最好的方法是使用NSOperation(或Swift 3 中的Operation)将原始数据转换为NSManagedObject 和在所有操作完成后保存该上下文。

我不能为每个Operation 使用单独的上下文,因为我的转换器会生成关系(并且它们只能从相同的上下文中访问)并且应用程序可能会运行多达 20 次转换,因此创建新上下文并不酷并在每次转换后保存。

所以我需要创建单独的OperationQueue 并确保其中的所有操作都从与上下文相同的线程执行,我不知道该怎么做。

我只有一个想法:将Operation.main() 中的所有内容都以context.perform { } 开头,但我并不认为这是一个好的解决方案。

我找到了similar thread at Stackoverflow,但答案已经过时,我发现接受的答案显然不正确。

【问题讨论】:

  • 你看到这个答案了吗? stackoverflow.com/a/16178742/294949
  • @danh,是的,但是据我所知,NSManagedContext 没有待处理操作的计数器,我怎么知道所有操作都已完成?你认为只做很多.perform { } 并在最后一个上解雇.perform { try context.save() } 会安全吗?所以它会在最后运行那个保存。
  • 操作队列有一个operationCount 属性。这样就够了吗?当 KVO 变为零时,您可能会收到通知。
  • @danh,上下文不是操作队列,您建议将上下文用作 NSOperation(就像您提供的答案一样)。或者我不明白这种方法,您可以发布任何示例吗?

标签: objective-c swift core-data nsoperation nsoperationqueue


【解决方案1】:

使用您自己的操作队列不是最好的方法。使用 Core Data,您需要通过mainQueueConcurrencyTypeprivateQueueConcurrencyType 使用内置的Core Data 并发,然后使用performperformAndWait。使用您描述的自定义操作队列将无法正常工作。

这将使用由 Core Data 管理的调度队列。您无法直接计算待处理操作的数量,但您可以一次添加一堆块,最后一个块可以在导入过程完成后执行任何需要发生的事情。在导入过程中而不是在最后保存也是一个好主意,以防止内存使用失控,除非您只导入少量数据。

【讨论】:

    【解决方案2】:

    我发现在Operation 中使用Core Data 的最佳方法是使用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()
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多