【问题标题】:Problem with NSFetchedResultsController and inserting recordsNSFetchedResultsController 和插入记录的问题
【发布时间】:2011-07-01 09:50:14
【问题描述】:

好的,这个让我发疯了,我已经尝试了所有可以在互联网上找到的可能的解决方案,但仍然没有。所以这是我的情况: 我有一个视图,上面有一个按钮。当它被触摸时,它会弹出一个客户列表供用户选择。当他们选择它时,我使用 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 已删除)。与用户信息(空) 2011-02-21 10:39:12.907 SalesPro[36203:207] * 终止 应用程序由于未捕获的异常 'NSRangeException',原因: '-[UITableView scrollToRowAtIndexPath:atScrollPosition:animated:]: 第 (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];
    }
}

这是我必须为我们的下一个版本(很快)解决的最大(最严重)缺陷。我将不胜感激任何帮助!谢谢!

【问题讨论】:

    标签: iphone objective-c ipad ios nsfetchedresultscontroller


    【解决方案1】:

    从修复泄漏的 NSFetchedResultsCONtroller 开始

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

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

    【讨论】:

    • 好的,谢谢。如果您查看我对我自己的问题的回答,我已将其删除。我是一名 .NET 开发人员,仍在学习 Objective-C 和 iPhone 开发,最让我感到困惑的是内存管理,但我已经掌握了窍门。
    【解决方案2】:

    好的,多亏了这个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];
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-21
      • 1970-01-01
      • 2013-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-28
      相关资源
      最近更新 更多