Как заполнить базовое хранилище данных программно?

Править: минимальный проект, который показывает катастрофический отказ, может быть загружен с испытания на ударную прочность при столкновении. Это было создано путем выбора шаблона проекта "navigation based with core data" в XCode и изменения, возможно, десяти строк.

Я имею, исчерпал волосы для получения по запросу с катастрофическим отказом, когда раздел и два объекта добавляются сразу.

Катастрофический отказ происходит в конце стандартной программы в вызове к [managedObjectContext save:&error].

Катастрофический отказ из-связанного исключение для NSArray:

Serious application error.  Exception was caught during Core Data change processing: *** -[NSCFArray objectAtIndex:]: index (1) beyond bounds (1) with userInfo (null)

Также, возможно, релевантный, когда исключение происходят, мой контроллер результата выборки controllerDidChangeContent: стандартная программа делегата находится в стеке вызовов. Это просто называет мое табличное представление endUpdate стандартная программа.

У меня теперь заканчиваются идеи. То, как я, как предполагается, вставляю больше чем один объект в базовое хранилище данных с использованием табличного представления, разделяет?

Вот стек вызовов:

#0  0x901ca4e6 in objc_exception_throw
#1  0x01d86c3b in +[NSException raise:format:arguments:]
#2  0x01d86b9a in +[NSException raise:format:]
#3  0x00072cb9 in _NSArrayRaiseBoundException
#4  0x00010217 in -[NSCFArray objectAtIndex:]
#5  0x002eaaa7 in -[UITableView(_UITableViewPrivate) _endCellAnimationsWithContext:]
#6  0x002def02 in -[UITableView endUpdates]
#7  0x00004863 in -[AirportViewController controllerDidChangeContent:] at AirportViewController.m:463
#8  0x01c43be1 in -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:]
#9  0x0001462a in _nsnote_callback
#10 0x01d31005 in _CFXNotificationPostNotification
#11 0x00011ee0 in -[NSNotificationCenter postNotificationName:object:userInfo:]
#12 0x01ba417d in -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:]
#13 0x01c03763 in -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:]
#14 0x01b885ea in -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:]
#15 0x01bbe728 in -[NSManagedObjectContext save:]
#16 0x000039ea in -[AirportViewController populateAirports] at AirportViewController.m:112

Вот код к стандартной программе. Я приношу извинения, потому что много строк, вероятно, не важны, но я допустил бы ошибку на той стороне. Катастрофический отказ происходит, когда он звонит [context save:&error]:

- (void) insertObjects
{
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity];

NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];

// If appropriate, configure the new managed object.
[newManagedObject setValue:@"new airport1" forKey:@"name"];
[newManagedObject setValue:@"???" forKey:@"code"];
[newManagedObject setValue:@"new country" forKey:@"country_name"];

newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[newManagedObject setValue:@"new airport2" forKey:@"name"];
[newManagedObject setValue:@"???" forKey:@"code"];
[newManagedObject setValue:@"new country" forKey:@"country_name"];


// Save the context.
NSError *error = nil;
if (![context save:&error]) {
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}
}

Примечание: разделы country_name. Кроме того, четыре NSFetchedResultsControllerDelegate стандартные программы как документируются и, как задано XCode:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo
       atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type) {
    case NSFetchedResultsChangeInsert:
        [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
        break;
// other cases omitted because not occurring in this crash            

}
}


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
   atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
  newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.tableView;

switch(type) {

    case NSFetchedResultsChangeInsert:
        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;
// other cases omitted because not occurring in this crash            
}
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}

5
задан Jean-Denis Muys 15 March 2010 в 09:13
поделиться

3 ответа

Мне кажется, это ошибка в Cocoa Touch. Конечно, я могу ошибаться. В любом случае нашел обходной путь.

Обход состоит в том, чтобы ничего не делать в четырех процедурах делегирования, но только в этом случае. В итоге я добавил BOOL massUpdate iVar, который я установил в значение true перед добавлением объектов, и которое я сбрасываю в значение false после вызова save.

В четырех процедурах делегата я тестирую massUpdate iVar. Если это правда, я ничего не делаю, кроме четвертого, где я перезагружаю всю таблицу.

Я получаю:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
if (massUpdate)
    return;
[self.tableView beginUpdates];
}


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
       atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
if (massUpdate)
    return;
    <snip normal implementation>
}


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
   atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
  newIndexPath:(NSIndexPath *)newIndexPath
{
if (massUpdate)
    return;
    <snip normal implementation>
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
if (massUpdate)
    [self.tableView reloadData];
else
    [self.tableView endUpdates];
}
3
ответ дан 14 December 2019 в 19:09
поделиться

Это не ошибка в Cocoa Touch. Эти методы делегирования используются постоянно и работают нормально.

Сначала вы должны установить точку останова на objc_exception_throw, а затем запустить ее в отладчике. Это приведет к остановке кода только до возникновения исключения и поможет сузить область, где это происходит.

Теперь я подозреваю, что ошибка возникает не в методах делегата, а из-за них. Судя по трассировке стека, я подозреваю, что у вас проблема либо в методе -numberOfSectionsInTableView: , либо в методе -tableView: numberOfRowsInSection: . Мне было бы очень интересно их увидеть.

ОБНОВЛЕНИЕ

Кажется, я должен исправиться. Похоже, что вы обнаружили ошибку в текущей реализации Core Data на Cocoa Touch. К счастью, эту конкретную ошибку, хотя и очень интересную, легко избежать.

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

Есть несколько способов обойти эту ошибку:

  1. Создавать по одному объекту Event за раз. Объекты на другом конце отношения и / или объекты в других таблицах, похоже, не влияют на эту ошибку. Вы даже можете создать несколько объектов в таблице событий после первого сохранения, но это первое сохранение после инициализации NSFetchedResultsController , похоже, вызывает проблему.
  2. Создайте объекты Event перед инициализацией NSFetchedResultsController . Это начальное обновление кеша имен раздела, которое вызывает ошибку, поэтому, если объекты существуют до кеша, оно не будет генерироваться.

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

1
ответ дан 14 December 2019 в 19:09
поделиться

Сбой здесь происходит в процедуре обновления вашего UITableView (и его последующей попытке анимировать). Вы не получаете последовательных вызовов insertRowsAtIndexPaths:withRowAnimation: и его родственников внутри блока beginUpdates/endUpdates. Под "согласованностью" я подразумеваю, что результаты таких процедур, как numberOfRowsInSection, должны изменяться в соответствии со вставками и удалениями.

Вызываете ли вы beginUpdates в controllerWillChangeContent:? Реализован ли у вас другой код, подробно описанный в документации NSFetchedResultsControllerDelegate в разделе "Typical Use?". В частности, реализуете ли вы controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:, как описано? Это была бы рутина, которую я больше всего подозреваю, учитывая ваш сбой.

1
ответ дан 14 December 2019 в 19:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: