iPhone4 iOS5 NSFetchedResultsController 如何在子类中正确设置委托?

Posted

技术标签:

【中文标题】iPhone4 iOS5 NSFetchedResultsController 如何在子类中正确设置委托?【英文标题】:iPhone4 iOS5 NSFetchedResultsController how to set the delegate properly in a subclass? 【发布时间】:2011-11-17 22:06:22 【问题描述】:

我有一个父类来处理所有 UITableview 持久数据管理和 UITableView 行管理。我已经从一个新的 XCode4 项目中复制了大部分代码,其中包含 UITableview 的持久数据。现在我正在尝试删除子类中的 tablerow,并且在删除行时不会调用任何委托方法,导致我的 tableview 处于不一致的状态。

我是否正确分配了代表?在这种情况下,谁是 NSFetechedResultsController 的代表? 我是否需要明确地将我的子类设为 NSFetchedResultsControllerDelegate?

这是我检索 NSFetchedResultsController 的方法,注意它是如何将 self 分配为委托的。

- (NSFetchedResultsController *)fetchedResultsController

    if (__fetchedResultsController != nil)
    
        return __fetchedResultsController;
    

    /*
     Set up the fetched results controller.
    */
    // Create the fetch request for the entity.
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:self.managedObjectContext];

    [fetchRequest setEntity:entity];

    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:fetchBatchSize];

    // Edit the sort key as appropriate.
//    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"startMinute" ascending:YES];
    NSArray *sortDescriptors;
    if(sortDescriptor2!=nil)
    
     sortDescriptors= [[NSArray alloc] initWithObjects:sortDescriptor,sortDescriptor2, nil];
    else
    
     sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    
    [fetchRequest setSortDescriptors:sortDescriptors];

    if(filterPredicate != nil)
    
        [fetchRequest setPredicate:filterPredicate];
    

    // 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:nil cacheName:@"Root"];
    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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
         */
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
//      abort();
    

    return __fetchedResultsController;
  

当需要删除表行时,不会调用任何委托方法,即使表行已被真正删除。为了调试问题,我添加了以下 2 个断言。第二个断言失败。

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath

    id delegate = self.fetchedResultsController.delegate;


    NSAssert(delegate!=nil,@"Tableview is not delegate of fetched results controller!");

//this assertion fails
    NSAssert([((UITableViewController*)self) isEqual:delegate],@"Tableview is not delegate of fetched results controller!");

    if (editingStyle == UITableViewCellEditingStyleDelete)
    


        // Delete the managed object for the given index path
        NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];

        [context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];

        // Save the context.
        NSError *error = nil;
        if (![context save:&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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
             */
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
//            abort();
        
       

谢谢!

【问题讨论】:

【参考方案1】:

设置NSFetchedResultsController 的代码看起来是正确的。它的委托设置为 self 意味着它的委托是您在其中创建它的对象。这看起来与 tableView 的委托相同。看起来您正确地删除了对象并保存了上下文。

您没有显示并且可能导致崩溃的原因是您对 NSFetchedResultsController 委托方法的处理。至少,您需要添加以下内容:

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller

    [self.tableView reloadData];

当 fetchedResultsController 检测到 managedObjectContext 发生变化时,这将强制表重新加载。

如果这不是您的问题,那么您需要发布更多代码,展示您如何配置类以及如何处理 fetchedResultsController 委托方法。

编辑:修正了不正确的签名 om controllerDidChangeContent:

【讨论】:

【参考方案2】:

您必须使用这些方法让NSFetchedResultsController 能够对表格视图中的更改做出反应:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller;

    // The fetch controller is about to start sending change notifications, so prepare the table view for updates.
    [self.tableView beginUpdates];



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

    switch(type) 
        case NSFetchedResultsChangeInsert:
            // your code for insert
        break;
        case NSFetchedResultsChangeDelete:
           // your code for deletion
        break;
    


- (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

    // The fetch controller has sent all current change notifications, so tell the table view to process all updates.
    [self.tableView endUpdates];

同时让 ViewController 使用 NSFetchedResultsController 监听 NSFetchedResultsControllerDelegate.

【讨论】:

以上是关于iPhone4 iOS5 NSFetchedResultsController 如何在子类中正确设置委托?的主要内容,如果未能解决你的问题,请参考以下文章

苹果怎么把壁纸保存到相册

带有 iOS 6 AVAudioPlayer 的 iPhone 4S 工作正常,但没有声音

iOS5 区域监控准确率

自动布局和 ios5

iOS 5 上的应用程序崩溃

新 iPad 是不是支持酷睿蓝牙,还是仅限于 iPhone 4S?