Nic*_*las 9 core-data ios swift swift5 combine
我想实现以下目标:每当有人触发 CoreData 保存(即NSManagedObjectContextDidSave
发送通知)时,我想根据更改的 NSManagedObject执行一些后台计算。具体示例:假设在一个笔记应用程序中,我想异步计算所有笔记中的总字数。
当前的问题在于 NSManagedObject 上下文显式绑定到线程,并且不鼓励您NSManagedObject
在该线程之外使用s。
我NSManagedObjectContext
在我的 s 中设置了两个SceneDelegate
:
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let backgroundContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.newBackgroundContext()
Run Code Online (Sandbox Code Playgroud)
我也都签署通过的通知NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)
,并正在接收保存的通知两次我只触发后一个 managedObjectContext.save()
。但是,这两个通知都是从同一个线程(即 UIThread)发送的,并且NSManagedObjects
用户字典中的所有通知都有一个.managedObjectContext
是 theviewContext
而不是backgroundContext
。
我的想法是根据关联NSManagedObjectContext
是否为后台来过滤通知,因为我认为通知也是在(私有)DispatchQueue 上发送的,但似乎所有通知都是在 UIThread 上发送的,并且从未使用过后台上下文.
关于如何解决这个问题的任何想法?这是一个错误吗?如何根据backgroundContext
正在关联的 DispatchQueue 上运行的下游任务检索通知?
tal*_*lis 14
您可以创建一个发布者,当 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
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以将要观察的对象传递给publisher(for:)
:
NotificationCenter.default
.publisher(for: .NSManagedObjectContextDidSave, object: backgroundMoc)
.sink(receiveValue: { notification in
// handle changes
})
Run Code Online (Sandbox Code Playgroud)
这只会侦听与后台托管对象上下文相关的通知,这意味着您可以安全地对该上下文的队列进行处理。
归档时间: |
|
查看次数: |
4422 次 |
最近记录: |