2017-07-26 63 views
0

我使用performBackgroundTask函数从firebase中提取数据,将其与已存储在Core Data中的数据进行比较,将新数据保存到Core Data中,并在完成时调用完成处理程序。核心数据和并发

我明白,核心数据不是线程安全的,但我试图同时做到这一点。

static func cache(completion: @escaping (Void) -> Void) { 
    CoreDataHelper.persistentContainer.performBackgroundTask { (context) in 
     let dispatchGroup = DispatchGroup() 
     // fetch previously saved Core Data from main thread (1) and filter them (2) 
     let newsSourceIDs = NewsSourceService.getSaved().filter{$0.isEnabled}.map{$0.id!} 
     let oldArticleURLs = ArticleService.getSaved().map{$0.url!} 
     // create firebase database reference 
     let ref = Database.database().reference() 
     Constants.Settings.timeOptions.forEach { time in 
      let timeRef = ref.child("time\(time)minutes") 
      newsSourceIDs.forEach { newsSourceID in 
       dispatchGroup.enter() 
       // pull from Firebase Database 
       timeRef.child(newsSourceID).observeSingleEvent(of: .value, with: { (snapshot) in 
         guard let newsSourceDict = snapshot.value as? [String: [String:String]] else { 
          return 
         } 
         newsSourceDict.values.forEach { articleDict in 
          dispatchGroup.enter() 
          if oldArticleURLs.contains(articleDict["url"]!) { 
           dispatchGroup.leave() 
           return 
          } 
          // create article entity with firebase data 
          let article = Article(context: context) 
          article.date = articleDict["date"] 
          article.source = newsSourceID 
          article.time = Int16(time) 
          article.title = articleDict["title"] 
          article.url = articleDict["url"] 
          article.urlToImage = articleDict["urlToImage"] 
          dispatchGroup.leave() 
         } 
        dispatchGroup.leave() 
       }) 
      } 
     } 
     // when done, save and call completion handler (3) 
     dispatchGroup.notify(queue: .main) { 
      do { 
       try context.save() 
       completion() 
      } catch { 
       fatalError("Failure to save context: \(error)") 
      } 
     } 
    } 

} 

从核心数据功能获取:

static func getSaved() -> [Article] { 
    let fetchRequest: NSFetchRequest<Article> = Article.fetchRequest() 
    do { 
     let results = try CoreDataHelper.managedContext.fetch(fetchRequest) 
     return results 
    } catch let error as NSError { 
     print("Could not fetch \(error)") 
    } 
    return [] 
} 
  1. 我可以从主线程中performBackgroundTask获取核心数据?
  2. 我应该与高级别filter功能或使用特殊的批量要求(我能做到这一点的同时?)
  3. 我如何使用dispatchGroup.notify(queue:)确定何时核心数据的创建和保存完整过滤器?

回答

0

我可以在performBackgroundTask期间从主线程获取核心数据吗?

如果您使用该方法,您可以从任何线程取。你不能在主线程上使用结果。 NSPersistentContainer提供viewContext属性以用于主线程。

我应该与高级别过滤功能过滤器或使用特殊的批量要求(我能做到这一点的同时?)

我有一个谓语做它在一个正常的非批处理请求。你提到的任何一种方式都是可能的。这取决于你需要什么样的提取和过滤。如果提取和过滤器需要很长时间才能运行,则批处理请求可能会很好。如果您的过滤规则无法在谓词中表达,则在提取后过滤结果可能会很好。

如何使用dispatchGroup.notify(队列:)来确定Core Data的创建和保存是否完成?

在您的forEach关闭后添加通知呼叫。如果你永远不会enter,它会立即执行。如果您执行enter,则在您将每个enterleave匹配时执行。

另一个细节:您的getSaved方法应该将托管对象上下文作为参数,并使用该上下文进行获取。否则,你在这里混合上下文。 performBackgroundTask会创建一个上下文,但在getSaved中使用的是另一个上下文。

+0

context.save()同步发生(或足够快),完成处理程序将能够访问数据? – caziz

+0

和哪个队列应该通知使用(不是.main?) – caziz

+0

保存是同步的。要通知的队列取决于通知块中的代码需要执行的操作。如果它正在更新UI,那么主队列。 –

0

处理核心数据并发的另一种方式(最简单但未优化)将使用一个“子”managedObjectContext和一个并发类型为private的设置新的MOC的父元素为您的main上的MOC线。

let privateMOC = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) 

privateMOC.parent = persistentManager.managedObjectContext 

privateMOC.perform { 
    do { 
     try privateMOC.save() 
    } catch let error as NSError { 
    } 
} 

您将执行所有所需的核心数据行为的.perform封闭的内部。当您运行privateMOC.save()时,更改将推送到主线程上的父级managedObjectContext。