NSFetchedResultsController находит неправильное количество объектов - PullRequest
2 голосов
/ 10 января 2020

Я вижу ситуацию, когда NSFetchRequest возвращает различное количество объектов в зависимости от того, выполняется ли он напрямую через NSManagedObjectContext или как часть построения NSFetchedResultsController.

Пример кода:

- (void)setupResultsController {
    NSError *error = nil;
    NSManagedObjectContext *ctx = [[DataManager sharedInstance] mainObjectContext];

    // Create a fetch request and execute it directly
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    NSEntityDescription *entity = [Song entityInManagedObjectContext:ctx];
    [fetchRequest setEntity:entity];
    [fetchRequest setFetchBatchSize:20];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"section" ascending:YES];
    NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    NSArray *sortDescriptors = @[sortDescriptor, nameDescriptor];

    [fetchRequest setSortDescriptors:sortDescriptors];
    NSArray *debugResults = [ctx executeFetchRequest:fetchRequest error:&error];
    NSLog(@"Count from context fetch: %lu", (unsigned long)debugResults.count);

    // Use the request to populate a NSFetchedResultsController
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
                                                             initWithFetchRequest:fetchRequest
                                                             managedObjectContext:ctx
                                                             sectionNameKeyPath:@"section"
                                                             cacheName:@"Detail"];
    [aFetchedResultsController performFetch:&error];
    NSLog(@"Count from results controller fetch: %lu", (unsigned long)[[aFetchedResultsController fetchedObjects] count]);

    _songResultsController = aFetchedResultsController;
}

Выполнение вышеуказанных результатов в сообщениях журнала:

2020-01-10 11: 05: 07.892772-0500 asb7 [12985: 105052] Подсчет из выборки из контекста: 10

2020-01-10 11: 05: 07.893259-0500 asb7 [12985: 105052] Подсчет из выборки контроллера результатов: 9

Разница между двумя выборками заключается в том, что NSFetchedResultsController отсутствует последний добавленный объект. Крайняя странность заключается в том, что после запуска приложения некоторое, казалось бы, случайное число раз, счетчики начинают совпадать, и новый объект извлекается.

Редактировать:

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

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

После еще нескольких экспериментов у меня есть объяснение ... если не решение Добавление новых объектов не меняет дату изменения моего файла .sqlite. Он обновляет .sqlite-shm и .sqlite-wal, но я предполагаю, что они не учитываются при оценке использования кэша. Использование touch из терминальной сессии устраняет проблему go для следующего запуска.

(Xcode 10.1, macOS 10.13.6, цель развертывания 10.3, iOS 12.1 симулятор и устройство 10.3.2)

Другое редактирование:

Я загрузил заархивированный каталог проекта, который демонстрирует проблему в https://github.com/PhilKMills/CacheTest

Что Я получаю это: первый запуск, 3 записи для обеих выборок; второй запуск, 6 и 3. Я считаю вполне вероятным, что это зависит от моих конкретных версий программного обеспечения, но в настоящий момент я не в состоянии выполнить обновление. Результаты других людей были бы наиболее интересны.

Примечание: без назначения делегата FR C проблема не появляется.

1 Ответ

2 голосов
/ 12 января 2020

Я подозревал, что проблема связана с использованием кэша FR C, хотя я не был уверен, почему это может быть. Поэтому одним из обходных путей может быть отказ от использования кэша FR C. Однако это не идеально.

После дальнейших экспериментов OP кажется, что проблема связана с отметкой времени в файле .sqlite, связанном с постоянным хранилищем. По логическому выводу (*), если FR C обнаруживает, что метка времени изменилась, он понимает, что его кэш может устареть, и перестраивает его - таким образом обнаруживая вновь добавленные объекты. Однако поскольку CoreData по умолчанию использует SQLite «режим журнала WAL» (см. здесь и здесь ), обновления базы данных записываются не в файл .sqlite, а в отдельный * Файл 1009 *: временная метка в файле .sqlite, следовательно, не изменяется, даже если база данных была обновлена. FR C продолжает использовать свой кэш, не подозревая о дополнительных объектах (или о других изменениях).

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

NSSQLitePragmasOption:@{@"journal_mode":@"DELETE"}

см. здесь .

(*) На самом деле это задокументировано здесь :

«Контроллер проверяет кэш, чтобы определить, является ли его содержимое все еще действительным. Контроллер сравнивает текущее имя объекта, версию объекта ha sh, дескрипторы сортировки и путь ключа раздела с теми, которые хранятся в кэше, а также дату модификации файла кэшированной информации и файла постоянного хранилища. ”

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