Многопоточный объект Core Data имеет пустые свойства в главном потоке после сохранения в фоновом потоке - PullRequest
2 голосов
/ 09 мая 2019

Как кратко описано в заголовке, после сохранения объекта в фоновом потоке его свойства в основном потоке остаются пустыми, например, строки "", числа 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.async, используя его objectID (NSManagedObjectID), пользовательские свойства все еще пусты.

Полагаю, я делаю что-то не так, но не могу понять, что это. Любой совет будет принята с благодарностью.

Заранее большое спасибо!

1 Ответ

2 голосов
/ 09 мая 2019

Проблема, я думаю, в том, что 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 из двух разных потоков.

Когда вы находитесь в главном потоке, вы можете общаться с Базовыми данными только через контекст main .Вам необходимо извлечь пользователя по его идентификатору из контекста main .Теперь вы можете посмотреть, как он заселен.

...