ios - CoreData严重应用错误

标签 ios objective-c core-data restkit restkit-0.20

每隔一段时间,我的应用程序就会收到以下错误消息。这发生在我的应用程序通过带有 CoreData 集成的 RestKit 接收回 Web 服务响应之后:

CoreData: error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  no object at index 3 in section at index 0 with userInfo (null)
2014-07-28 12:48:26.348 Identify[43074:60b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'no object at index 3 in section at index 0'
*** First throw call stack:
(
    0   CoreFoundation                      0x0270e1e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x0248d8e5 objc_exception_throw + 44
    2   CoreData                            0x006d7dbe -[NSFetchedResultsController objectAtIndexPath:] + 398
    3   Identify                            0x0002d03e -[WTSRecordListViewController configureCell:atIndexPath:] + 190
    4   Identify                            0x0002c5f6 -[WTSRecordListViewController controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:] + 646
    5   CoreData                            0x006df296 -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 5238
    6   Foundation                          0x02160049 __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke + 40
    7   CoreFoundation                      0x02769f04 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20
    8   CoreFoundation                      0x026c1efb _CFXNotificationPost + 2859
    9   Foundation                          0x02099e41 -[NSNotificationCenter postNotificationName:object:userInfo:] + 98
    10  CoreData                            0x005e1a13 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 83
    11  CoreData                            0x00680faf -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:] + 367
    12  CoreData                            0x005dceb8 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 2152
    13  CoreData                            0x005e02fc -[NSManagedObjectContext save:] + 140
    14  Identify                            0x000d1ffd __61-[NSManagedObjectContext(RKAdditions) saveToPersistentStore:]_block_invoke + 125
    15  CoreData                            0x0060584f developerSubmittedBlockToNSManagedObjectContextPerform + 95
    16  CoreData                            0x0060578c -[NSManagedObjectContext performBlockAndWait:] + 140
    17  Identify                            0x000d1b83 -[NSManagedObjectContext(RKAdditions) saveToPersistentStore:] + 739
    18  Identify                            0x00048d28 __80-[WTSObjectManager getRecordsForUser:forGroup:startIndex:limit:success:failure:]_block_invoke_2 + 2456
    19  Identify                            0x0014f551 __66-[RKObjectRequestOperation setCompletionBlockWithSuccess:failure:]_block_invoke244 + 97
    20  libdispatch.dylib                   0x02f747b8 _dispatch_call_block_and_release + 15
    21  libdispatch.dylib                   0x02f894d0 _dispatch_client_callout + 14
    22  libdispatch.dylib                   0x02f77726 _dispatch_main_queue_callback_4CF + 340
    23  CoreFoundation                      0x0277343e __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 14
    24  CoreFoundation                      0x026b45cb __CFRunLoopRun + 1963
    25  CoreFoundation                      0x026b39d3 CFRunLoopRunSpecific + 467
    26  CoreFoundation                      0x026b37eb CFRunLoopRunInMode + 123
    27  GraphicsServices                    0x0367c5ee GSEventRunModal + 192
    28  GraphicsServices                    0x0367c42b GSEventRun + 104
    29  UIKit                               0x0114df9b UIApplicationMain + 1225
    30  Identify                            0x0000279d main + 141
    31  libdyld.dylib                       0x031be701 start + 1

一旦进入此状态,如果不删除应用程序并将其重新添加到设备上,就无法修复它。我正在使用苹果为核心数据获取结果 Controller 定义的库存样板代码:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    NSLog(@"begin updates");
    [self.tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    switch(type) {
        case NSFetchedResultsChangeInsert:
            NSLog(@"inserting section");
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            NSLog(@"deleting section");
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

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

    switch(type) {
        case NSFetchedResultsChangeInsert:
            NSLog(@"inserting row");
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            NSLog(@"deleting row");
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeUpdate:
            NSLog(@"update row");
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            NSLog(@"move row");
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    NSLog(@"end updates");
    [self.tableView endUpdates];
}

configureCell 方法看起来像这样,当我从 fetchedResultsController 获取记录时失败了:

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
    //if a nil cell was passed into this method, just return
    if (!cell) {
        return;
    }

    WTSRecordCell *recordCell = (WTSRecordCell *)cell;
    WTSRecord *record = [self.fetchedResultsController objectAtIndexPath:indexPath];
...
}

以下代码是我创建 fetchedResultsController 的地方。在我的应用程序中,我创建了两个单独的 WTSRecordListViewController 实例,一个带有获取请求,按用户返回“记录”,另一个按组返回“记录”。

- (NSFetchedResultsController *)fetchedResultsController
{
    if (_fetchedResultsController == nil) {
        WTSUserInfo *userInfo = [WTSConfiguration sharedConfig].orgConfig.userInfo;
        NSFetchRequest *fetchRequest = nil;
        if (self.type == RecordListTypeUser) {
            fetchRequest = [[WTSObjectManager sharedManager] fetchRequestForRecordsByUser:userInfo.username];
        } else if (self.type == RecordListTypeGroup) {
            fetchRequest = [[WTSObjectManager sharedManager] fetchRequestForRecordsByGroup:userInfo.groupId];
        }

        NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"createDt" ascending:NO];
        NSArray *sortDescriptors = @[sortDescriptor];
        [fetchRequest setSortDescriptors:sortDescriptors];

        // Edit the section name key path and cache name if appropriate.
        // nil for section name key path means "no sections".
        NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"sectionName" cacheName:nil];
        aFetchedResultsController.delegate = self;
        self.fetchedResultsController = aFetchedResultsController;

        NSError *error = nil;
        if (![self.fetchedResultsController performFetch:&error]) {
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            DDLogWarn(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }

    return _fetchedResultsController;
}

我不确定我在这里做错了什么,以及这是否是 Apple 代码中的某种错误。正如我之前所说,这种情况只会偶尔发生一次(可能是当后端核心数据处于某种非常特定的状态时,因为在它发生后我似乎永远无法重新创建它)。

编辑: 经过一些额外的搜索,我找到了导致问题的代码。在 RestKit 中的 success block 中,我进行了网络服务调用,我遍历了一些设置特定值的数据库对象。当我完成对值的迭代后,我调用 [context saveToPersistentStore];该调用是导致获取的结果 Controller 方法被触发的原因。如果我注释掉 saveToPersistentStore 调用,我不会得到异常。

RestKit 中的 success block 在主线程上执行。此外,根据我的打印输出,该 block 在 controllerWillChangeContent:controllerDidChangeContent: 方法之外执行。

编辑 2: 以下代码是我通过 RestKit 进行 Web 服务调用的地方。在其中,我遍历映射数组中返回的对象,设置如何获取记录以供以后清除孤儿。我还对未从获取请求返回的记录执行获取请求并清除标志(也用于孤立清除)。

在我的 RestKit 成功 block 中,我没有检查以确保映射请求中返回的对象被删除或从获取请求返回的对象在修改它们之前被删除。这是我需要做的事情吗?

[self.rkObjectManager getObjectsAtPathForRouteNamed:kWTSRecordsRouteName object:nil parameters:params success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
        NSArray *records = [mappingResult array];

        //we put the record ids we get back in the request in a set
        NSMutableSet *recordIds = [NSMutableSet new];

        //set in the record how this was fetched
        NSManagedObjectContext *context = nil;
        for (WTSRecord *record in records) {
            [recordIds addObject:record.recordId];
            if (!context) {
                context = record.managedObjectContext;
            }

            if (groupId) {
                record.fetchedViaGroup = [NSNumber numberWithBool:YES];
            } else {
                record.fetchedViaUser = [NSNumber numberWithBool:YES];
            }
        }

        //if the startIndex is 0, the user is refreshing.  We clear the 'fetchedVia*' field for any records that DID NOT come back in this request
        if (startIndex == 0) {
            NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"WTSRecord"];
            if (groupId) {
                request.predicate = [NSPredicate predicateWithFormat:@"recordId != nil AND fetchedViaGroup == 1 AND pendingDelete == 0"];
            } else {
                request.predicate = [NSPredicate predicateWithFormat:@"recordId != nil AND fetchedViaUser == 1 AND pendingDelete == 0", user];
            }

            NSArray *records = [context executeFetchRequest:request error:nil];
            for (WTSRecord *record in records) {
                if (![recordIds containsObject:record.recordId]) {
                    if (groupId) {
                        record.fetchedViaGroup = [NSNumber numberWithBool:NO];
                    } else {
                        record.fetchedViaUser = [NSNumber numberWithBool:NO];
                    }
                }
            }
        }

        //save the changes to the persistent store
        [context saveToPersistentStore:nil];

        success(records);
        dispatch_semaphore_signal(sem);
    } failure:^(RKObjectRequestOperation *operation, NSError *error) {
        failure(error);
        dispatch_semaphore_signal(sem);
    }];

编辑 3 我在上面的代码片段中找到了导致问题的代码:

//set in the record how this was fetched
            NSManagedObjectContext *context = nil;
            for (WTSRecord *record in records) {
                [recordIds addObject:record.recordId];
                if (!context) {
                    context = record.managedObjectContext;
                }

//              if (groupId) {
//                  record.fetchedViaGroup = [NSNumber numberWithBool:YES];
//              } else {
//                  record.fetchedViaUser = [NSNumber numberWithBool:YES];
//              }
            }

注释掉以下行后,我可以毫无问题地调用 saveToPersistentStore。我在这里做错了什么?修改 mappingResult 中返回的对象并将更改保存到本地数据存储的可接受方法是什么?

最佳答案

在不知道您如何设置 View Controller 的所有细节的情况下,我的最佳答案是,这个问题在我设置时经常发生。

一个。将 TableView 插入 View Controller

B.在同一个 View Controller 中保存数据。

无论我的情况如何,如果您要将数据保存在一个也有 TableView 的 View 中,您需要删除代码中的以下方法:

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

-(void) controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type

出现的错误是因为fetched结果突然发生了tableview还不能识别的变化ie。 “索引 0 部分的索引 3 处没有对象”。

如果你想让表响应变化,你需要添加一个refresh方法并在数据保存后调用它(听起来很明显,但 Stack Exchange 有很多人不这样做不明白)即。 [self refresh]; 所以在你的 -(IBAction *) save: (id) sender 方法(或者 void 方法,如果你使用它来保存),调用它从那里。

希望这对以后的任何人都有帮助。

关于ios - CoreData严重应用错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25000559/

相关文章:

ios - UICollectionView 单元格的水平重新排序

ios - 访问 ManagedObjectContext

ios - 父/子 NSManagedObjectContext 的正确实现

ios - 快速字符串操作

ios - 操作完成了 isFinished=YES 没有被它所在的队列启动

ios - 如何从保存在文档目录中的 plist 中读取数据并在新 View 中将其显示为 pdf?

objective-c - 为iphone设置nsdate格式

iphone - 在 willAnimateRotationToInterfaceOrientation 中禁用动画?

iphone - 有未命名的部分或手动附加关系以避免这种情况?

ios - 当数据在两个字典中时,如何区分数据和json?