【问题标题】:How to populate a core data store programmatically?如何以编程方式填充核心数据存储?
【发布时间】:2010-03-14 23:46:18
【问题描述】:

编辑:可以从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];
}

【问题讨论】:

    标签: uitableview core-data crash


    【解决方案1】:

    在我看来这是 Cocoa Touch 中的一个错误。我当然可能是错的。无论如何,我找到了解决办法。

    解决方法是在四个委托例程中什么都不做,但仅限于这种情况。我最终添加了一个 BOOL massUpdate iVar,在添加对象之前将其设置为 true,并在调用 save 后重置为 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];
    }
    

    【讨论】:

      【解决方案2】:

      这里的崩溃是在您的 UITableView 的更新例程中(以及随后的动画尝试)。您没有在 beginUpdates/endUpdates 块中收到对 insertRowsAtIndexPaths:withRowAnimation: 及其亲属的连贯调用。 “连贯”是指来自numberOfRowsInSection 等例程的结果需要以与插入和删除一致的方式进行更改。

      您在controllerWillChangeContent: 中拨打beginUpdates 吗?您是否实现了NSFetchedResultsControllerDelegate 文档中“典型用途”下详述的其他代码?特别是,您是否按照所述实施controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:?考虑到你的崩溃,这将是我最怀疑的例行程序。

      【讨论】:

      • 是的,我有。或者更确切地说,XCode 有。我没有从插入的项目模板中更改它们。 (使用 Core Data 的导航类应用)。
      • 我检查了文档,我的 4 个NSFetchedResultsControllerDelegate 例程实际上与文档中给出的一样。
      • 我完全简化了上面的崩溃案例,并添加了四个委托例程。
      【解决方案3】:

      这不是 Cocoa Touch 中的错误。这些委托方法不断被使用,并且工作得很好。

      首先,您应该在 objc_exception_throw 上放置一个断点,然后在调试器中运行它。这将导致代码在异常发生之前停止,并帮助缩小异常发生的范围。

      现在,我怀疑错误不是在委托方法中发生,而是因为它们。从堆栈跟踪中,我怀疑您的 -numberOfSectionsInTableView: 方法或 -tableView:numberOfRowsInSection: 方法存在问题。我很想看看这些。

      更新

      看来我必须纠正。看来您确实在 Cocoa Touch 上的 Core Data 的当前实现中发现了一个错误。幸运的是,这个特殊的错误虽然很有趣,但很容易避免。

      问题在于在使用空数据库创建 NSFetchedResultsController 之后创建两个 Event 对象。具体来说,节索引的创建似乎无法处理这种情况。

      有几种方法可以解决这个错误:

      1. 一次创建一个事件对象。关系另一端的对象和/或其他表中的对象似乎不会影响此错误。您甚至可以在第一次保存后在事件表中创建多个对象,但在初始化 NSFetchedResultsController 之后的第一次保存似乎是导致问题的原因。
      2. 在初始化NSFetchedResultsController 之前创建事件对象。这是导致错误的部分名称缓存的初始更新,因此如果对象在缓存之前存在,则它不会抛出。

      我不得不承认,这是我见过的更有趣的崩溃之一,我将针对它提出雷达。我邀请您也这样做,以便可以在(希望)操作系统的下一个版本中更正它。

      【讨论】:

      • 我确实在抛出异常时中断了,从我发布的调用堆栈中可以看出。委托方法也与 XCode 模板提供的方法相同。我很高兴证明这是一个 Cocoa Touch 错误。崩溃很容易从基于核心数据导航的普通应用程序中复制出来:1- 创建这样的项目 2- 将字符串属性添加到提供的示例事件实体。例如,将其命名为“kind” 3- 修改 fetchedResultsController 以在 kind 上创建部分(将调用更改为 initWithFetchRequest 以传递 @"kind" 而不是 nil
      • 4- 添加香草部分标签委托方法:- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { return [[[fetchedResultsController sections] objectAtIndex:section] name]; }
      • 5- 修改insertNewObject 例程以创建2 个事件而不是1 个:[newManagedObject setValue:@"lunch" forKey:@"kind"]; newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context]; [newManagedObject setValue:[NSDate date] forKey:@"timeStamp"]; [newManagedObject setValue:@"lunch" forKey:@"kind"]; 就是这样。以空状态启动应用,点击“+”按钮,观看崩溃
      • 按要求我有: - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [[fetchedResultsController section] count]; }
      • 和 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { id sectionInfo = [[fetchedResultsController section] objectAtIndex:section]; NSUInteger n = [sectionInfo numberOfObjects];返回 n; }
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-16
      • 2019-01-01
      • 2016-05-15
      • 2015-05-24
      • 1970-01-01
      • 1970-01-01
      • 2011-01-14
      相关资源
      最近更新 更多