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

时间:2019-05-09 16:54:20

标签: swift core-data ios10

正如标题中简短描述的那样,将对象保存到后台线程后,其属性在主线程上为空,例如字符串为"",数字为0等。

这里有一些代码!

用户类别:

@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,但是即使我尝试使用DispatchQueue.main.asyncobjectID),{@ {1}}来获取NSManagedObjectID块中的用户对象,用户属性还是空的。

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

非常感谢!

1 个答案:

答案 0 :(得分:2)

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

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 上下文中提取用户。现在您可以看看它是如何填充的。