保存后台上下文后的 NSFetchedResultsController NSRangeException

Posted

技术标签:

【中文标题】保存后台上下文后的 NSFetchedResultsController NSRangeException【英文标题】:NSFetchedResultsController NSRangeException after saving background context 【发布时间】:2013-01-30 06:49:12 【问题描述】:

我自己无法重现这一点,所以我只需要收集一些我收集的错误日志即可。这个实在想不通。崩溃发生时有 2 个 FetchedResults 控制器处于活动状态。一个在主屏幕上显示每个类别中有多少项目,一个用于处理您深入研究的类别。我所知道的其余回溯是,在向下钻取视图上插入对象时总是会发生此问题。有任何想法吗?有人至少可以确认问题在于更新主屏幕上的单元格而不是在详细屏幕上插入单元格吗?

错误:

** 请注意,有趣的部分似乎是范围 0,3 和边界 [0 .. 1] 在我收到的关于此问题的 5-10 条日志中始终相同 **

应用特定信息: * 由于未捕获的异常 \'NSRangeException\' 导致应用程序终止,原因:\'* -[NSArray indexOfObject:inRange:]: range 0, 3 超出范围 [0 .. 1]\ '

最后一个异常回溯: 0 CoreFoundation 0x35ad988f __exceptionPreprocess + 163 1 libobjc.A.dylib 0x336fd259 objc_exception_throw + 33 2 CoreFoundation 0x35acda09 -[NSArray indexOfObject:inRange:] + 205 3 核心数据 0x32e8e73d -[_NSDefaultSectionInfo indexOfObject:] + 133 4 CoreData 0x32e8f299 -[NSFetchedResultsController indexPathForObject:] + 149 5 CoreData 0x32e956f5 -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 4361

创建控制器:

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:self.groupID];

NSFetchedResultsController 代码:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
   atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
  newIndexPath:(NSIndexPath *)newIndexPath


    switch(type) 
        case NSFetchedResultsChangeInsert:
            [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;
        case NSFetchedResultsChangeMove:
            [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
            break;
    


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller

    [self.tableView endUpdates];


- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller

    [self.tableView beginUpdates];

配置单元:

-(void)configureCell:(MessageRow *)cell atIndexPath:(NSIndexPath *)indexPath 

    MymanagedObject *myobj = [self.convoController objectAtIndexPath:indexPath];
    cell.title = myobj.title
 

请注意,在我的主屏幕上,所有操作基本上都以相同的方式完成,除了在更新时重新加载 tableview 行而不是配置

case NSFetchedResultsChangeUpdate:
            [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
        break;

【问题讨论】:

【参考方案1】:

我遇到了类似的问题。

诀窍是跟踪屏幕上的内容和屏幕外的内容。我在NSFetchedResultsControllerDelegate 回调中检查这些条件,如果 FRC 引用屏幕外数据,则不进行任何更新。

您可以稍后使用viewWillAppear 中的触发器“赶上”更新。

【讨论】:

所以您认为最好的解决方案是停止在 viewWillDissapear 上成为 NSFetchedResultsControllerDelegate 并在 viewWillAppear 上重新打开/刷新?你建议如何赶上? [tableView 刷新数据]? 我也试过了,但不太可靠。如果委托在主线程上不活动,则以某种方式设置和取消设置委托并不总是有效。 明确一点,在我的情况下,有两个控制器和两个独立的代表。细节控制器从主控制器的关系中获取对象。每当我进行添加时,它都会增加父对象上的计数器,从而触发主控制器上的委托。所以不是同一个对象被更新了两次

以上是关于保存后台上下文后的 NSFetchedResultsController NSRangeException的主要内容,如果未能解决你的问题,请参考以下文章

在后台队列中保存临时托管对象上下文

应用程序冻结 CoreData 保存

在后台线程上安全保存 Core Data 托管对象上下文的正确方法?

iOS 在后台保存主线程 NSManagedObjectContext 更改

合并到主线程上下文时,在后台线程上下文中更新的可转换属性未保存

当应用程序后台运行时,Core Data 无法通过区域监控保存上下文