在后台线程上保存后,多线程核心数据 object 在主线程上具有空属性

Multithreaded Core Data object has empty properties on main thread after saving on background thread

正如标题中所描述的,我在后台线程上保存了一个object后,它的属性在主线程上是空的,比如字符串是"",数字是0等等

这是一些代码!

用户class:

@objc(User)
class User: NSManagedObject {

    @NSManaged var id: Int32
    @NSManaged var email: String
}

发生实际保存的 UserRepository:

func saveUser(fromJSON json: Any, onSuccess: ((User) -> Void)?, onFailure: ((Error) -> Void)?) {
    dataManager.persistentContainer.performBackgroundTask { context in
        context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
        let user = self.userFactory.user(fromJSON: json, inContext: context)
        // print(user.id) and print(user.email) output correct data
        do {
            try context.save()
            DispatchQueue.main.async {
                // print(user.id) and print(user.email) show 0 and ""
                onSuccess?(user)
            }
        } catch let error {
            DispatchQueue.main.async {
                onFailure?(error)
            }
        }
    }
}

其中dataManager是这样配置的:

class CoreDataManager {

    private let modelName: String

    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: modelName)
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }

            container.viewContext.automaticallyMergesChangesFromParent = true
            container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
        })
        return container
    }()

    init(modelName: String) {
        self.modelName = modelName
    }
}

我猜 userFactory 没那么重要,但为了完整起见:

class UserFactory {

    func user(fromJSON json: Any, inContext context: NSManagedObjectContext) -> User {
        guard
            let jsonDictionary = json as? [String: Any?],
            let userDictionary = jsonDictionary["user"] as? [String: Any?],
            let id = userDictionary["id"] as? Int32,
            let email = userDictionary["email"] as? String
        else {
            fatalError("Could not parse User object.")
        }

        let user = User(context: context)
        user.id = id
        user.email = email
        return user
    }
}

请参阅上面片段中关于打印语句的注释,这些语句指出了用户属性良好的地方以及它们显示 0"".

的地方

我知道我不能在线程之间传递 NSManagedObjects,但即使我尝试使用 objectIDDispatchQueue.main.async 块中获取用户 object (NSManagedObjectID), 用户属性仍然是空的。

我想我做错了什么,但不能指手画脚。任何建议将不胜感激。

非常感谢!

我认为问题在于 Core Data 不是线程安全的。我知道你知道这一点 - 毕竟,这就是你创建特殊背景上下文的原因 - 但它具有非常严格的含义。你是说:

dataManager.persistentContainer.performBackgroundTask { context in
    context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
    let user = self.userFactory.user(fromJSON: json, inContext: context)
    do {
        try context.save()
        DispatchQueue.main.async {
            print(user) // <--

你不能那样做。您仍在谈论保存到后台上下文中的 user,但您已经切换了线程。不可以。你不能从两个不同的线程谈论这个 user

一旦进入主线程,与 Core Data 对话的唯一方式就是通过 main 上下文。您需要通过用户 ID 从 main 上下文中 fetch 用户。现在您可以看看它是如何填充的。