NSFetchedResultsControllerDelegate & UITableViewDelegate 行为

Posted

技术标签:

【中文标题】NSFetchedResultsControllerDelegate & UITableViewDelegate 行为【英文标题】:NSFetchedResultsControllerDelegate & UITableViewDelegate Behavior 【发布时间】:2011-02-28 06:51:24 【问题描述】:

我遇到了 FRC 和 TableView 委托的行为似乎不一致:

performFetch 的初始调用会导致按预期调用委托方法。但是,如果我更新 FRC 的 baseFetch 上的谓词,然后再次调用 performFetch,则永远不会调用 FRC 委托方法,并且不会使用新数据更新 TableView。为了强制表格更新和显示新数据,我显式调用 reloadData。 对performFetch 的初始调用正确地构建了TableView 的部分。如果我更改 FRC(新的 FCR 有不同的 fetchRequestsectionNameKeyPath)并再次调用 performFetch,表格和部分会更新以匹配新的结果,和预期的一样。但是,如果我随后返回原始 FRC 并调用 performFetch,则这些部分具有正确的名称,但具有先前 FRC 的部分/行结构。为了强制更新这些部分,我明确调用 reloadSectionIndexTitles

我的感觉是,如果发生变化,只要我们调用 performFetch,代表就应该被解雇。显式调用 reloadDatareloadSectionIndexTitles 似乎是一个昂贵且不必要的步骤,因为这就是代表存在的目的。我错过了什么吗?

以下是相关代码:

NSFetchedResultsControllerDelegate

    - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller 
    // The fetch controller is about to start sending change notifications, so prepare the table view for updates.
    [self.myTable beginUpdates];



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

    switch(type) 

        case NSFetchedResultsChangeInsert:
            [self.myTable insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

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

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[self.myTable cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [self.myTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            // Reloading the section inserts a new row and ensures that titles are updated appropriately.
            [self.myTable reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
            break;
    




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

    switch(type) 

        case NSFetchedResultsChangeInsert:
            [self.myTable insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.myTable deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
    



- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller 
    // The fetch controller has sent all current change notifications, so tell the table view to process all updates.
    [self.myTable endUpdates];

UITableViewDelegate

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
    // Return the number of sections.
    return [[currentFCR sections] count];


- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section  
    id <NSFetchedResultsSectionInfo> sectionInfo = [[currentFCR sections] objectAtIndex:section];
    return section.name;


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    // Return the number of rows in the section.
    id <NSFetchedResultsSectionInfo> sectionInfo = [[currentFCR sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];


// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) 
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
    

    // Configure the cell...
    [self configureCell:cell atIndexPath:indexPath];

    return cell;

正在抓取

- (NSFetchedResultsController *)FRC1 

    if (FRC1 != nil) 
        return FRC1;
    

    NSFetchRequest *request = [[DataProvider instance] getFetch];
    [[DataProvider instance] sortListByFirstSort:request];
    [request setFetchBatchSize:20];

    FRC1 = [[NSFetchedResultsController alloc] initWithFetchRequest:request 
                                                                   managedObjectContext:[[DataProvider instance] managedObjectContext] 
                                                                     sectionNameKeyPath:@"groupName" 
                                                                              cacheName:nil];
    return FRC1;    



- (NSFetchedResultsController *)FRC2 

    if (FRC2 != nil) 
        return FRC2;
    

    NSFetchRequest *request = [[DataProvider instance] getFetch];
    [[DataProvider instance] sortListBySecondSort:request]; 


 [request setFetchBatchSize:20];

        FRC2 = [[NSFetchedResultsController alloc] initWithFetchRequest:request 
                                                       managedObjectContext:[[DataProvider instance] managedObjectContext] 
                                                         sectionNameKeyPath:@"name" 
                                                                  cacheName:nil];
        return FRC2;    

    

    -(void)doFetch
    NSError *error;
    if (![currentFCR performFetch:&error]) 
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    
    [self.myTable reloadData];
    [self.myTable reloadSectionIndexTitles];

【问题讨论】:

【参考方案1】:

NSFetchedResultsControllerDelegate 方法 controllerWillChangeContent: 等,只有在 NSFetchedResultsController 实例上设置了委托时才会调用。我没有在您的代码中看到任何代表设置。

初始表加载(或显式调用 reloadTable)将导致调用 UITableViewDataSource 方法,从 currentFCR 获取数据。如果 NSFetchedResultsController 上没有设置委托,则不会调用 NSFetchedResultsControllerDelegate 方法。

此外,NSFetchedResultsControllerDelegate 方法仅在 NSManagedObjectContext 中的任何相关托管对象发生更改(或添加/删除)时才被调用。

如果你想替换 NSFetchedResultsController 对象,你需要明确地重新加载表。

【讨论】:

感谢克里斯的信息。我确实设置了委托,但没有显示该方法。这里是:-(void)setFCR:(NSFetchedResultsController *)fcr [currentFCR setDelegate:nil]; currentFCR = fcr; [currentFCR setDelegate:self]; 所以,如果我理解你正确地改变谓词 - 即对托管对象的过滤器 - 不会导致真正的变化? 好吧,您实际上并没有更改现有 NSFetchedResultsController 实例上的谓词或 fetchRequest(这是不允许的)。您正在用一个新实例替换您的实例,因此您需要告诉表视图刷新。分成两个 UITableViewController 可能会更好,每个获取请求一个?

以上是关于NSFetchedResultsControllerDelegate & UITableViewDelegate 行为的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 应用程序中调用 performFetch 后,是不是需要手动更新表视图?