Как я должен гарантировать, что результаты выборки из другого потока во вложенных контекстах актуальны, когда сохранения выполняются асинхронно в фоновом режиме? - PullRequest
0 голосов
/ 31 декабря 2018

Я прочитал следующие Различия в поведении между executeBlock: и executeBlockAndWait:? Но не смог найти ответ на мой вопрос.

Следующий код взят из видео RayWenderlich .В частности, в 10: 05 код что-то примерно так:

class CoreDataStack {
    var coordinator : NSPersistentStoreCoordinator

    init(coordinator: NSPersistentStoreCoordinator){
        self.coordinator = coordinator
    }
    // private, parent, in background used for saving
    private lazy var savingContext : NSManagedObjectContext = {
        let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
        moc.persistentStoreCoordinator = coordinator
        return moc
    }()

    lazy var mainManagedObjectedContext : NSManagedObjectContext = {
        let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        moc.parent = self.savingContext
        return moc
    }()

    func saveMainContext() {
        guard savingContext.hasChanges || mainManagedObjectedContext.hasChanges else {
            return
        }

        mainManagedObjectedContext.performAndWait {
            do {
                try mainManagedObjectedContext.save()
            }catch let error{
                fatalError(error.localizedDescription)
            }
        }

        savingContext.perform {
            do {
                try self.savingContext.save()
            }catch let error{
                fatalError(error.localizedDescription)
            }
        }
    }
}

Из того, что я понимаю, происходит то, что основной контекст просто передает изменения в свойродительский контекст, который является частным, фоновым контекстом.Это происходит синхронно.

Затем родительский частный контекст выполняет фактическое сохранение для sqlite в фоновом потоке асинхронно .Короче говоря, это очень помогает нам с производительностью.Но как насчет целостности данных?!

Представьте себе, если бы я сделал это:

let coredataManager = CoreDataStack()
coredataManager.saveMainContext() // save is done asynchronously in background queue
coredataManager.mainManagedObjectedContext.fetch(fetchrequest) 

Как я могу гарантировать, что моя выборка считывает самые последние и обновленные результаты?

Если мы выполняем наши записи асинхронно, то нет ли шансов, что другое чтение одновременно может привести к неожиданным результатам, то есть результаты изменений сохранения могут или не могут быть?

РЕДАКТИРОВАТЬ: Я сделал улучшение с кодом ниже.Я могу сделать свое сохранение в параметре completeHandler.Но это не решает всей проблемы.Что делать, если я делаю fetchRequest из mainQueue где-то еще, который не знает, что одновременно происходит сохранение?

enum SaveStatus{
    case noChanges
    case failure
    case success
}


func saveMainContext(completionHandler: (SaveStatus -> ())) {
    guard savingContext.hasChanges || mainManagedObjectedContext.hasChanges else {
        completionHandler(.noChanges)
        return
    }

    mainManagedObjectedContext.performAndWait {
        do {
            try mainManagedObjectedContext.save()
        }catch let error{
            completionHandler(.failure)
            fatalError(error.localizedDescription)
        }
    }

    savingContext.perform {
        do {
            try self.savingContext.save()
            completionHandler(.succes)
        }catch let error{
            completionHandler(.failure)
            fatalError(error.localizedDescription)
        }
    }
}

1 Ответ

0 голосов
/ 06 июня 2019

Все вызовы на mainManagedObjectContext будут синхронными и, следовательно, будут блокироваться.Если вы наберете saveMainContext() и сразу после этого позвоните mainManagedObjectedContext.fetch(fetchrequest), запрос на выборку не будет выполнен, пока запрос на сохранение не будет выполнен, даже если запросы на сохранение / выборку поступают из разных очередей (см. Параграф о FIFO в вашей ссылке выше).

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

Дочерний контейнер - это контейнер - он будет хранить ваши последние изменения в памяти (в отличие от сохраненных на диске - то есть постоянных).работа контейнера).

Реальная проблема здесь заключается в том, что ваш CoreDataStack должен реализовать шаблон синглтона, чтобы предотвратить создание нескольких версий одних и тех же контейнеров (которые по-прежнему технически находятся в одном потоке и, следовательно, сериализуются, но обращаются кконтейнеры не были бы безопасны для нитей).Другими словами, каждый раз, когда вы создаете экземпляр CoreDataStack(), вы создаете новые savingContext и mainManagedObjectedContext.

Вместо этого создаете его один раз.

class CoreDataStack {

    var coordinator: NSPersistentStoreCoordinator

    public static let sharedInstance = CoreDataStack()

    private override init() {
        self.coordinator = NSPersistantStoreCoordinator()
    }

    ...
    rest of your code here
    ...
}

И вызываетекак это:

CoreDataStack.sharedInstance.saveMainContext()

(см. эту ссылку re: 'У ребенка есть те же объекты, что и у родителя?')

Единственный случай, когда ребенок не будет синхронизирован с родителем, - это когда у вас есть несколько детей, обращающихся к одному и тому же родителю, но здесь это не так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...