Как стать автором
Обновить

Комментарии 3

В первом же куске кода у вас ошибка. Вы не имеете права вызывать замыкание в текущей очереди. Только в очереди контекста.

A private queue context (as defined by a NSManagedObjectContextConcurrencyType.privateQueueConcurrencyType) creates its own queue upon initialization and can be used only on that queue. Because the queue is private and internal to the NSManagedObjectContext instance, it can only be accessed through the perform(:) and the performAndWait(:) methods.

Отсюда.

Если бы Вы сделали правильно, то проблем бы не возникло:

func enqueueBackgroundWriteBlock(block: (NSManagedObjectContext) -> Void) {
    let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
    context.parent = readContext
    context.perform { // Вы пропустили вот это!
      block(context)
      do {
        try context.save()
      } catch {
        context.rollback()
      }
    } 
}

CoreData потоконебезопасна.

Это заблуждение. Методы выше, как раз и сделаны для того, чтобы кордатой пользоваться потокобезопасно.

Вот примерно так нежелание читать документацию приводит к выполнению абсолютно бесполезной работы. Вы проделали все то, что уже есть у Apple из коробки. Только у Apple тот код более надежен и учитывает кучу различных специфических ситуаций.

Удачи в чтении документации и приведении своего кода к рекомендациям Эпла.

Здравствуйте! Спасибо за Ваши замечания, указанный фрагмент кода из статьи действительно содержит опечатку - block обязательно должен быть вызван внутри блока perform на фоновом контексте, вместе с safe и rollback.

Реализация варианта с child/parent контекстами выглядела именно так как Вы написали, соответствующие правки будут внесены в статью!

Касаемо Вашего утверждения:

Если бы Вы сделали правильно, то проблем бы не возникло:

Это не совсем так. В этом разделе статьи указано, почему этот метод было решено не использовать:

Однако контекст все еще тащит информацию через readContext и как следствие через main очередь, поэтому child/parent подход в моем случае не подошел.

В своем ответе я буду основываться на книге от objc.io по Core Data (глава «Complexity of Nested Contexts»), тредах stack overflow, такие как этот, а также на результах профилирования работы приложения.

При сохранении child контекста, информация переносится из child в parent контекст, во время этого процесса блокируются как child, так и parent контексты. Так как в нашем случае parent контекст имеет тип .mainQueueConcurrencyType, эта операция блокирует основной поток и может заметно влиять на отзывчивость UI. Это подтверждается как при реальном использовании приложения, так и профилированием.

CoreData потоконебезопасна.

Это заблуждение. Методы выше, как раз и сделаны для того, чтобы кордатой пользоваться потокобезопасно.

Соглашусь, что определение потокобезопасности может трактоваться по-разному, и обобщать весь фреймворк этим термином не совсем корректно, это тоже будет отражено в статье!

Но, в то же время, ссылаясь на документацию Apple, CoreData действительно проектировалась с учетом многопоточного программирования, но для этого нужно соблюдать правила работы с инструментами, предоставляемыми фреймворком. Так как во фреймворке существуют инструменты для решения проблемы потоконебезопасности, можно сделать вывод, что определенные компоненты потоконебезопасны по умолчанию.

Хочу добавить, что я не претендую на истину и приветствую любую дискуссию, ведь так и выявляется правда! Спасибо!

Хороший коммент. Глубокий. Видно, что работу над ошибками Вы провели. Уважаю.

Вы правы про блокировку контекста полностью. Но глядите какая штука. Вы ведь не обязаны дочерний бекграунд поток делать от родительского main. Можно же и наоборот? Родительский у вас в бекграунде и работает напрямую с хранилищем, а вот дочерний от него работает на главном потоке и весь UI завязан на дочерний, а не на родительский контекст. С CoreData работал давно, но почему-то помню именно такую схему. Мне казалось, что как раз в книжке, на которую Вы ссылаетесь эту схему я и видел. Эта схема должна работать быстрее, потому что она отбирает ресурсы главного потока только на мёрдж изменений из родительского контекста в дочерний (без блокировки на запись в стор). Но это не точно :)

Вот как раз ответ из треда, на который Вы ссылаетесь, о том, что я и говорю.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий