Как программно заполнить основное хранилище данных? - PullRequest
5 голосов
/ 15 марта 2010

Редактировать: минимальный проект, демонстрирующий сбой, можно загрузить с crashTest . Он был создан путем выбора шаблона проекта «навигация на основе основных данных» в 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 <NSFetchedResultsSectionInfo>)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];
}

Ответы [ 3 ]

3 голосов
/ 15 марта 2010

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

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

В четырех подпрограммах делегата я тестирую 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];
}
1 голос
/ 15 марта 2010

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

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

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

UPDATE

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

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

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

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

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

1 голос
/ 15 марта 2010

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

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

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