【问题标题】:CoreData backgroundContext save doesn't save and is lostCoreData backgroundContext保存不保存丢失
【发布时间】:2020-12-24 09:27:10
【问题描述】:

我在后台保存内容时遇到问题。所以我有两个与 RxSwift 链接的操作。 第一项被保存,当我在主上下文中获取它们时,所有信息都在那里。 但是在第二个操作中,我获取了相同的对象,并使用更多信息对其进行了更新。 现在无论我在那里做什么,它都不会更新实体。

这是我的核心数据代码:

class CoreDataStack {
    static let modelName = "Mo_Application"
    static let model: NSManagedObjectModel = {
        let modelURL = Bundle(for: CoreDataStack.self).url(forResource: modelName, withExtension: "momd")!
        return NSManagedObjectModel(contentsOf: modelURL)!
    }()

    lazy var mainContext: NSManagedObjectContext = {
        return self.storeContainer.viewContext
    }()

    lazy var storeContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: CoreDataStack.modelName, managedObjectModel: CoreDataStack.model)
        container.loadPersistentStores { (_, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        }
        return container
    }()

    func newDerivedContext() -> NSManagedObjectContext {
        return self.storeContainer.newBackgroundContext()
    }

    func saveContext() throws {
        try? self.saveContext(mainContext)
    }

    func saveAndWaitContext(_ context: NSManagedObjectContext) throws {
        if context != mainContext {
            try self.saveAndWaitDerivedContext(context)
            return
        }

        context.performAndWait {
            try? context.save()
        }
    }

    func saveAndWaitDerivedContext(_ context: NSManagedObjectContext) throws {
        context.performAndWait {
            try? context.save()
            try? self.saveAndWaitContext(self.mainContext)
        }
    }
}

我在第一次操作中做了什么:

let context = coreDataStack.newDerivedContext()

let object = Foo(context: context)
object.name = "Name 1"

try coreDataStack.saveAndWaitDerivedContext(context)
maybe(.success(self)) (RxSwift to complete)

在第二个操作中,我首先获取对象:

let context = coreDataStack.newDerivedContext()
let fetchRequest: NSFetchRequest<Foo> = Foo.fetchCreatedBy(user: user)
let objects: [Foo] = try context.fetch(fetchRequest)
let object = objects.first { $0.id == self.id }
if let object = object {
    object.name = "Name updated"
}
try coreDataStack.saveAndWaitDerivedContext(context)
maybe(.success(self))

当我现在对我的 mainContext 进行 fetch 时,旧值仍然存在。

当我调用我的 saveAndWaitDerivedContext 时,背景上下文 hasChanges。但是当我保存 mainContext 时它没有变化吗?这正常吗?

我做错了什么?

【问题讨论】:

    标签: ios swift core-data


    【解决方案1】:

    来自NSPersistentContainer.newBackgroundContext()上的文档:

    这个新上下文将直接与NSPersistentStoreCoordinator 关联,并设置为自动使用NSManagedObjectContextDidSave 广播。

    这提示您的后台上下文直接写入持久存储。如果您不将在后台上下文中所做的更改合并到其中,主上下文将不会知道这一点。

    您应该从后台上下文中收听NSManagedObjectContextDidSave 通知,并将其中的更改合并到主上下文中。

    func saveAndWaitDerivedContext(_ context: NSManagedObjectContext) throws {
        let observer = NotificationCenter.default.addObserver(forName: .NSManagedObjectContextDidSave, object: context, queue: .main) { (notification) in
            self.mainContext.mergeChanges(fromContextDidSave: notification)
        }
        context.performAndWait {
            try? context.save()    
        }
        NotificationCenter.default.removeObserver(observer)
    }
    

    【讨论】:

    • 会尝试的。我发现当我在 performAndWait 块中进行更改时,一切正常。我们不允许对执行块之外的对象进行更改吗?
    • > 我们不允许对执行块之外的对象进行更改吗? .privateQueue 并发类型修改属于 NSManagedObjectContext 的托管对象(这适用于您的后台上下文),您应该只在 performBlock / performBlockAndWait 块内修改 MO。
    【解决方案2】:

    正如前面的回答所说,因为NSPersistentContainernewBackgroundContext() 函数返回一个将直接与NSPersistentStoreCoordinator 关联的新上下文,您可以通过设置告诉您的主上下文自动合并保存到其持久存储的更改automaticallyMergesChangesFromParent 属性到true

    lazy var mainContext: NSManagedObjectContext = {
        self.storeContainer.viewContext.automaticallyMergesChangesFromParent = true
        return self.storeContainer.viewContext
    }()
    

    正如automaticallyMergesChangesFromParent 文档所述:

    一个布尔值,指示上下文是否自动 合并保存到其持久存储协调器或父级的更改 上下文

    如果上面的代码不起作用,也许你应该在NSPersistentContainer加载它的持久存储之后设置这个属性:

    lazy var storeContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: CoreDataStack.modelName, managedObjectModel: CoreDataStack.model)
        container.loadPersistentStores { (_, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
            container.viewContext.automaticallyMergesChangesFromParent = true
        }
        return container
    }()
    

    【讨论】:

    • 合并来自parentContext的更改,而不是来自持久存储的更改。
    • @EugeneDudnyk 我用automaticallyMergesChangesFromParent 属性的文档更新了我的答案。
    • @user1007522 我在一个与您的设置完全相同的项目上进行了尝试,它可以工作。它会给你任何错误吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多