NSFetchedResultsController - представление таблицы фида, в то время как фоновое обновление того же самого постоянного хранилища вызывает тупик

Все еще работаем над преобразованием приложения из загрузки информации каждый раз, когда оно использует или отображает ее, для кэширования ее по телефону с использованием CoreData (любезно предоставлено MagicalRecord). Это на iOS 7

Поскольку у нас нет настроенной системы передачи данных для автоматического обновления кэшированных данных телефона при каждом изменении данных на бэкэнде, я думал последние несколько месяцев (как мы работали над другими аспектами приложения), как управлять хранением локальной копии данных на телефоне и иметь возможность хранить самые свежие данные в кэше.

Я понял это, пока я все еще получаю данные:-(Я могу использовать кешированный в телефоне кэш данных CoreData для отображения и использования, и просто использовать выборку данных для обновления базы данных на телефоне.

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

По сути, каждый из обычных объектов данных в приложении, вместо того, чтобы содержать все свойства объекта внутри, содержит только objectID базового объекта CoreData и, возможно, внутренний идентификатор приложения, а все остальные свойства являются динамическими и получены из CoreData. объект и передается через него (большинство свойств доступны только для чтения, а обновления выполняются путем массовой перезаписи основных данных, переданных в JSON)

Как это:

- (NSString *)amount
{
    __block NSString *result = nil;

    NSManagedObjectContext *localContext = [NSManagedObjectContext MR_newContext];

    [localContext performBlockAndWait:^{
        FinTransaction  *transaction = (FinTransaction *)[localContext existingObjectWithID:[self objectID] error:nil];

        if (nil != transaction)
        {
            result = [transaction.amount stringValue];
        }
    }];

    return result;
}

Иногда есть один, который нужно установить, и они выглядят так:

- (void)setStatus:(MyTransactionStatus)status
{
    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
        FinTransaction *transaction = (FinTransaction *)[localContext existingObjectWithID:[self objectID] error:nil];

        if (nil != transaction)
        {
            transaction.statusValue = status;
        }

    } completion:^(BOOL success, NSError *error){}];
}

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

Проблема заключается в том, что, если пользователь прокручивает исходные извлеченные данные, извлеченные контроллером результатов, загружает данные и загружает представление таблицы, а фоновый поток обновляет те же объекты базовых данных в фоновом режиме, возникают взаимоблокировки. Это не те же самые объекты, которые выбираются и перезаписываются (когда возникает взаимоблокировка), т. Е. Не то, что объект ID 1 читается и записывается, а то, что используется одно и то же постоянное хранилище данных.

Каждый доступ, чтение или запись, происходит в MR_saveWithBlock или же MR_saveWithBlockAndWait (запись / обновление данных), в зависимости от обстоятельств, и [localContext executeBlock:] или [localContext executeBlockAndWait:], в зависимости от ситуации. Каждое отдельное чтение или запись имеет свои NSManagedObjectContext, Я не видел ни одного, где бывают случайные изменения, ожидающие изменения, и фактические места, которые он блокирует и блокирует, не всегда одинаковы, но всегда связаны с чтением основного потока из того же постоянного хранилища, что и фоновый поток, используемый для обновить данные.

Контроллер полученных результатов создается следующим образом:

_frController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                    managedObjectContext:[NSManagedObjectContext MR_rootSavingContext]
                                                      sectionNameKeyPath:sectionKeyPath
                                                               cacheName:nil];

а затем performFetch готово.

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

Пока я пользуюсь MagicalRecord в большинстве случаев я открыт для комментариев, ответов и т. д. с или без (прямой CD) с использованием MagicalRecord.

1 ответ

Решение

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

Этот подход подробно описан в Сессии 211 WWDC 2013 - "Оптимизация и отладка производительности основных данных", которую вы можете найти на сайте разработчиков Apple для WWDC 2013.

Чтобы использовать этот подход с MagicalRecord, вам необходимо рассмотреть возможность использования предстоящего выпуска MagicalRecord 3.0 с ClassicWithBackgroundCoordinatorSQLiteMagicalRecordStack (да, это имя нуждается в работе!). Он реализует подход, изложенный в сеансе WWDC, хотя вы должны знать, что в вашем проекте потребуются изменения для поддержки MagicalRecord 3, и что он также еще не совсем выпущен.

По сути, что вы в конечном итоге это:

  • 1 x Контекст основного потока: Вы используете это для заполнения вашего пользовательского интерфейса, а также для выбранных контроллеров результатов и т. Д. Никогда не вносите изменения в этом контексте.
  • 1 x Контекст приватной очереди: внесите все изменения, используя сохраненные методы на основе блоков, - они автоматически переходят в этот контекст и сохраняются на диск.

Я надеюсь, что это имеет смысл - определенно посмотрите сеанс WWDC - они используют несколько великолепных анимированных диаграмм, чтобы объяснить, почему этот подход быстрее (и не должен блокировать основной поток так же, как тот, который вы используете сейчас).

Я рад вдаваться в подробности, если вам это нужно.

Другие вопросы по тегам