NSFetchedResultsController снова не соответствует кешу - PullRequest
2 голосов
/ 22 марта 2011

Это еще один случай, когда вызов performFetch на NSFetchedResultController прерывается с исключением, говорящим:

ФАТАЛЬНАЯ ОШИБКА: Постоянный кэш информации раздела не соответствует текущей конфигурации. Вы незаконно мутировали запрос выборки NSFetchedResultsController, его предикат или дескриптор его сортировки, не отключая кэширование и не используя + deleteCacheWithName:

За исключением того, что я этого не делал. Чтобы понять проблему, посмотрите на предикат:

предикат: (collection.identifier == "; root" AND doc.status == 0);

Теперь посмотрите на несоответствующее содержимое кэша:

cached objects were: (
  "MyDocument 'PL00002.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00003.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00003.pdf' in set '/' (#2147483647)",
  "MyDocument 'PL00002.pdf' in set '/' (#2147483647)"
)
fetched objects are: (
  "MyDocument 'PL00002.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00003.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00004.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00003.pdf' in set '/' (#2147483647)",
  "MyDocument 'PL00002.pdf' in set '/' (#2147483647)",
  "MyDocument 'PL00004.pdf' in set '/' (#2147483647)"
)

Действительно, есть два новых подходящих объекта. Причина в том, что два новых объекта (PL00004.jpeg и PL00004.pdf) ранее имели свойство status на значение <> 0, и я изменил их status обратно на 0 в коде, потому что они начали удовлетворять некоторому внешнему условию (не вызывается действием пользователя).

Для полноты вот код, вызывающий исключение:

- (void) reloadData
{
  NSError *error = nil;
  if (![[self fetchedResultsController] performFetch:&error]) {
    abort(); // I have actually better error handling
  }       
  [_gridView reloadData];
}

также обратите внимание:

  • нет других NSFetchedResultController использует кэш с тем же именем
  • _gridView не является UITableView, хотя он также отображает коллекцию сущностей. Эта деталь не должна быть актуальной.
  • ни один из NSFetchedResultController методов делегатов никогда не вызывается, особенно когда я изменяю статус двух моих объектов на 0
  • все работает нормально, если я очищаю кэш до performFetch: (или без кэширования).
  • Я вызываю этот метод reloadData на моем контроллере по окончании периодической проверки того внешнего состояния, о котором я упоминал выше.
  • NSFetchedResultController создается с nil sectionNameKeyPath.
  • Как объяснено, я никогда не изменяю fetchedResultsController: ни предикат, ни запрос на выборку, ни порядок сортировки, ничего.

Я не думаю, что должен получить это исключение просто потому, что новые объекты теперь удовлетворяют предикату. Удаление кеша позволяет избежать исключения, но пока я не понимаю, почему я должен это делать, я не считаю это исправлением .

Есть идеи, что мне здесь не хватает?

Редактировать: вот запрашиваемая информация:

Код для установки NSFetchedResultController. Когда пользователь выбирает коллекцию, это называется:

- (void) displayCollection:(Collection *)aSet
{
    self.currentCollection = aSet;      
    NSFetchRequest *fetchRequest = [[self fetchedResultsController] fetchRequest];
    NSString *collectionIdentifier = @";root"; // no selection is like "select root collection";
    if (aSet) {
        collectionIdentifier = aSet.identifier;
    }
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"collection.identifier == %@ and doc.status == %d", collectionIdentifier, upToDate];
    [fetchRequest setPredicate:predicate];

    [NSFetchedResultsController deleteCacheWithName:cacheName];

    NSError *error = nil;
    if (![[self fetchedResultsController] performFetch:&error]) {
        abort(); // whatever
    }       
    [_gridView reloadData];
}

fetchedResultsController было инициализировано так:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"DocInCollection" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"collection.identifier == %@ and doc.status == %d", @";root", upToDate];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"position" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:cacheName];

Код, который вызывается при обновлении doc.status:

- (void) setUpToDate
{
  self.status = [NSNumber numberWithInt:upToDate];
  NSError *error = nil;
  [[self managedObjectContext] save:&error];
  if (error) { } // whatever
  [[self managedObjectContext] processPendingChanges];
}

cacheName - это глобальная константа NSString, upToDate - это константа перечисления (0).

Наконец, модель немного сложна из-за других сущностей, которые используются приложением. Вот упрощенная версия:

модель http://emberapp.com/jdmuys/images/untitled-1/sizes/m.png

1 Ответ

0 голосов
/ 22 марта 2011

Я бы посмотрел на ваше sectionNameKeyPath и его отношение, если оно есть, к атрибуту, который мутировал. Из сообщения об ошибке может показаться, что ваши разделы меняются не так, как контроллер.

Не уверен, связано ли это вообще, но я бы нервничал из-за любого утверждения, в котором есть точка с запятой без экранирования. Этот бит предиката ";root" вызывает у меня зуд. Конечно, любые проблемы с ним должны появляться во время компиляции, но все же ...

Обновление:

Из болезненного любопытства я связал воедино тестовую модель данных и некоторый код, используя приведенное выше. Следующее даже не скомпилируется:

NSManagedObject *cMO=[NSEntityDescription insertNewObjectForEntityForName:@"Collection" inManagedObjectContext:self.moc];
NSManagedObject *dMO=[NSEntityDescription insertNewObjectForEntityForName:@"Doc" inManagedObjectContext:self.moc];
[cMO setValue:@";root" forKey:@"identifier"];
[dMO setValue:[NSNumber numberWithInt:0] forKey:@"status"];
[cMO setValue:dMO forKey:@"doc"];
NSFetchRequest *fetch=[[NSFetchRequest alloc] init];
[fetch setEntity:[NSEntityDescription entityForName:@"Collection" inManagedObjectContext:self.moc]];
NSPredicate *p=[NSPredicate predicateWithFormat:@"identifier==";root" AND doc.status==0"];
[fetch setPredicate:p];
NSArray *a=[self performFetch:fetch];
NSLog(@"%@",a);

Изменение предиката на:

NSPredicate *p=[NSPredicate predicateWithFormat:@"identifier == ';root' AND doc.status == 0"];

... компилируется и работает правильно. Я предлагаю вам изменить ";root" на ';root' хотя бы для устранения неполадок.

...