gpt4 book ai didi

ios - 如何: Using Combine to react to CoreData changes in the background

转载 作者:行者123 更新时间:2023-12-01 15:24:26 25 4
gpt4 key购买 nike

我想实现以下目标:每当有人触发 CoreData 保存(即发送 NSManagedObjectContextDidSave 通知),我想执行一些 背景 基于更改的 NSManagedObject 的计算。具体示例:假设在一个笔记应用程序中,我想异步计算所有笔记中的单词总数。
当前的问题在于 NSManagedObject 上下文显式绑定(bind)到线程并且不鼓励您使用 NSManagedObject。在这个线程之外。
我设置了两个 NSManagedObjectContext s 在我的 SceneDelegate :

let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let backgroundContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.newBackgroundContext()
我还通过 NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave) 订阅了通知。并且在我只触发一个 managedObjectContext.save() 后收到两次保存通知.但是,这两个通知都是从同一个线程(即 UIThread)发送的,所有 NSManagedObjects在用户字典中有一个 .managedObjectContext这是 viewContext而不是 backgroundContext .
我的想法是根据关联的 NSManagedObjectContext 是否过滤通知。是背景通知,因为我假设通知也是在(私有(private))DispatchQueue 上发送的,但似乎所有通知都是在 UIThread 上发送的,并且从未使用过后台上下文。
关于如何解决这个问题的任何想法?这是一个错误吗?如何根据 backgroundContext 检索通知在关联的 DispatchQueue 上运行下游任务?

最佳答案

您可以创建一个发布者,当 Core Data 中与您相关的内容发生更改时,它会通知您。

我为此写了一篇文章。 Combine, Publishers and Core Data.

import Combine
import CoreData
import Foundation

class CDPublisher<Entity>: NSObject, NSFetchedResultsControllerDelegate, Publisher where Entity: NSManagedObject {
typealias Output = [Entity]
typealias Failure = Error

private let request: NSFetchRequest<Entity>
private let context: NSManagedObjectContext
private let subject: CurrentValueSubject<[Entity], Failure>
private var resultController: NSFetchedResultsController<NSManagedObject>?
private var subscriptions = 0

init(request: NSFetchRequest<Entity>, context: NSManagedObjectContext) {
if request.sortDescriptors == nil { request.sortDescriptors = [] }
self.request = request
self.context = context
subject = CurrentValueSubject([])
super.init()
}

func receive<S>(subscriber: S)
where S: Subscriber, CDPublisher.Failure == S.Failure, CDPublisher.Output == S.Input {
var start = false

synchronized(self) {
subscriptions += 1
start = subscriptions == 1
}

if start {
let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context,
sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = self

do {
try controller.performFetch()
let result = controller.fetchedObjects ?? []
subject.send(result)
} catch {
subject.send(completion: .failure(error))
}
resultController = controller as? NSFetchedResultsController<NSManagedObject>
}
CDSubscription(fetchPublisher: self, subscriber: AnySubscriber(subscriber))
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
let result = controller.fetchedObjects as? [Entity] ?? []
subject.send(result)
}

private func dropSubscription() {
objc_sync_enter(self)
subscriptions -= 1
let stop = subscriptions == 0
objc_sync_exit(self)

if stop {
resultController?.delegate = nil
resultController = nil
}
}

private class CDSubscription: Subscription {
private var fetchPublisher: CDPublisher?
private var cancellable: AnyCancellable?

@discardableResult
init(fetchPublisher: CDPublisher, subscriber: AnySubscriber<Output, Failure>) {
self.fetchPublisher = fetchPublisher

subscriber.receive(subscription: self)

cancellable = fetchPublisher.subject.sink(receiveCompletion: { completion in
subscriber.receive(completion: completion)
}, receiveValue: { value in
_ = subscriber.receive(value)
})
}

func request(_ demand: Subscribers.Demand) {}

func cancel() {
cancellable?.cancel()
cancellable = nil
fetchPublisher?.dropSubscription()
fetchPublisher = nil
}
}

}

关于ios - 如何: Using Combine to react to CoreData changes in the background,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60495494/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com