NSFetchedResultsController和插入记录的问题

时间:2011-02-21 16:31:28

标签: iphone objective-c ipad ios nsfetchedresultscontroller

好吧,这个让我疯狂,我已经尝试了我可以在互联网上找到的所有可能的解决方案,但仍然没有。所以这是我的情况: 我有一个视图,上面有一个按钮。当触摸它时,它会弹出一个客户列表供用户选择。当他们选择它时,我使用fetchedresultscontroller来获取与客户关联的部件并将其显示在tableview中。这一切都运作良好。问题是如果我选择了客户A并插入新零件,然后转到客户B并插入新零件,当我重新选择客户A并尝试插入另一个零件时,应用程序崩溃时出现以下错误:

  

*断言失败 - [UITableView _endCellAnimationsWithContext:],/ SourceCache / UIKit_Sim / UIKit-1447.6.4 / UITableView.m:976   2011-02-21 10:39:12.896   SalesPro [36203:207]认真   应用程序错误。一个例外是   抓住了代表   NSFetchedResultsController期间   调用-controllerDidChangeContent:。   无效更新:行数无效   在第0节中。行数   包含在现有部分之后   更新(1)必须等于   包含在其中的行数   更新前的部分(1),加或   减去插入的行数或   从该部分删除(1插入,   0已删除)。用户信息(null)   2011-02-21 10:39:12.907   SalesPro [36203:207] * 终止   应用程序由于未捕获的异常   'NSRangeException',原因:   “ - [UITableView的   scrollToRowAtIndexPath:atScrollPosition:动画:]:   第(1)行超出界限(1)的部分   (0)'

处理客户选择的代码

-(void) CustomerSelectedRaised:(NSNotification *)notif
{
    NSLog(@"Received Notification - Customer Selected");
    selectedCustomer = (Customer *)[notif object];
    [self buildCustomerInfoText];
    if (popoverController != nil) {
        [popoverController dismissPopoverAnimated:YES];
    }

    fetchedResultsController = nil;

    NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) {
        //Update to handle error appropriately
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1); //fail
    }

    [self.partsListGrid reloadData];
}

FetchedResultsController代码

#pragma mark -
#pragma mark Fetch results controller

- (NSFetchedResultsController *)fetchedResultsController {
    if (fetchedResultsController != nil) {
        return fetchedResultsController;
    }

    //set-up fetched results controller
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"PartsList" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];
    [fetchRequest setFetchBatchSize:20];
    NSLog(@"TAMS ID: %@", selectedCustomer.customerTAMSID);
    [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"customerTAMSID == %@", selectedCustomer.customerTAMSID]]; 

    //set to sort by customer name
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"sortOrder" ascending:YES];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];   
    [fetchRequest setSortDescriptors:sortDescriptors];

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] 
                                                             initWithFetchRequest:fetchRequest 
                                                             managedObjectContext:[self managedObjectContext] 
                                                             sectionNameKeyPath:nil cacheName:nil];
    [aFetchedResultsController setDelegate:self];
    [self setFetchedResultsController:aFetchedResultsController];

    //clean-up
    [aFetchedResultsController release];
    [fetchRequest release];
    [sortDescriptor release];
    [sortDescriptors release];

    //return results
    return fetchedResultsController;
}

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

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    // In the simplest, most efficient, case, reload the table view.
    [[self partsListGrid] endUpdates];
}

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

    UITableView *tableView = self.partsListGrid;

    switch(type) {

        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

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

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath withHeight:tableView.rowHeight];
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

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

    switch(type) {
        case NSFetchedResultsChangeInsert:
            [self.partsListGrid insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeDelete:
            [self.partsListGrid deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeMove:
            break;
        case NSFetchedResultsChangeUpdate: 
            break;
        default:
            break;
    }
}

添加零件代码

-(void) addScannedPart:(Part *)part 
{
    // Check to see if entered part is already in list
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *partsEntity = [NSEntityDescription entityForName:@"PartsList" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:partsEntity];

    NSPredicate *predicate = [NSPredicate predicateWithFormat: @"customerTAMSID == %@ AND lineAbbreviation == %@ AND partNumber == %@", selectedCustomer.customerTAMSID, part.lineAbbrev, part.partNumber];
    [fetchRequest setPredicate:predicate];

    NSError *error = nil;
    NSArray *fetchedParts = [managedObjectContext executeFetchRequest:fetchRequest error:&error];

    if ([fetchedParts count] == 0) {
        //Create a new instance of the entity managed object by the fetched results controller
        NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
        NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity];

        NSLog(@"Entity Name: %@", [entity name]);

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

        //Add fields to Managed Object
        int sortOrder = [[fetchedResultsController fetchedObjects] count];
        sortOrder++;

        [newManagedObject setValue:[part lineAbbrev] forKey:@"lineAbbreviation"];
        [newManagedObject setValue:[part partNumber] forKey:@"partNumber"];
        [newManagedObject setValue:[NSNumber numberWithInt:[[part orderQty] intValue]] forKey:@"orderQuantity"];
        [newManagedObject setValue:selectedCustomer.customerTAMSID forKey:@"customerTAMSID"];
        [newManagedObject setValue:[NSNumber numberWithInt:sortOrder] forKey:@"sortOrder"];

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

        //reload Customer list 
        NSIndexPath *insertionPath = [fetchedResultsController indexPathForObject:newManagedObject];
        [self.partsListGrid selectRowAtIndexPath:insertionPath animated:YES scrollPosition:UITableViewScrollPositionTop];
        [self.partsListGrid reloadData];
    }
}

这是我在下一个版本中发现的最大(最严重)缺陷(很快就会发现)。我很感激所有的帮助!谢谢!

2 个答案:

答案 0 :(得分:2)

首先修复泄漏的NSFetchedResultsCOntroller

-(void) CustomerSelectedRaised:(NSNotification *)notif
{
    /*...*/
    fetchedResultsController = nil;
    /*...*/
}

您不应该有多个NSFetchedResultsControllers都报告对同一个tableview的更改。我认为这就是正在发生的事情。除非取消分配NSFRController,否则它将向其委托报告更改

答案 1 :(得分:1)

好的,感谢这个SO thread,看起来我有它的工作:

我通过CustomerSelectedRaised方法编辑,如下所示:

-(void) CustomerSelectedRaised:(NSNotification *)notif
{
    NSLog(@"Received Notification - Customer Selected");
    selectedCustomer = (Customer *)[notif object];
    [self buildCustomerInfoText];
    if (popoverController != nil) {
        [popoverController dismissPopoverAnimated:YES];
    }
    //this is the new code
    NSFetchRequest *fetchRequest = [[self fetchedResultsController] fetchRequest];
    NSEntityDescription *entity = nil;
    entity = [NSEntityDescription entityForName:@"PartsList" inManagedObjectContext:managedObjectContext];
    [fetchRequest setEntity:entity];
    [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"customerTAMSID == %@", selectedCustomer.customerTAMSID]]; 

    [NSFetchedResultsController deleteCacheWithName:nil];
    // end of new code

    NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) {
        //Update to handle error appropriately
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1); //fail
    }

    [self.partsListGrid reloadData];
}