在使用 NSFetchedResultsController 更新 UITableView 时将来自 Web 的数据保存到 Core Data

Posted

技术标签:

【中文标题】在使用 NSFetchedResultsController 更新 UITableView 时将来自 Web 的数据保存到 Core Data【英文标题】:Saving data from the web into Core Data while updating UITableView with NSFetchedResultsController 【发布时间】:2013-07-04 17:09:10 【问题描述】:

我正在使用 HTTP GET 方法从网络中提取数据。然后我从网络上获取 JSON 数据并将其保存到 CoreData。我有一个带有自定义UITableViewCellsUITableView,它使用NSFetchedResultsController 进行了更新。

但是,当我去保存数据时,tableView 只会更新屏幕上已经存在的内容,当我向下滚动时,tableView 会变成白色。

在日志中我收到了一些错误:

CoreData:错误:严重的应用程序错误。在调用 -controllerDidChangeContent: 期间,从 NSFetchedResultsController 的委托中捕获了异常。尝试使用 userInfo (null) 删除并重新加载相同的索引路径 (length = 2, path = 0 - 43)

这是NSFetchedResultsControllerDelegatedidChangeObject 方法中使用的代码:

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.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

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

关于我能做什么的任何想法?

谢谢!

【问题讨论】:

【参考方案1】:

你的呢?

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

您可以使用我的,它肯定可以工作,我在几个项目中使用过它。 只需让你的 tableViewController 子类化这个。 然后初始化并将您的 fetchedResultsController 传递给您的 tableViewController 并在那里调用:

[self setupFetchedResultsController:yourFetchResultsController];

SMCoreDataTableViewController.m:

#import "SMCoreDataTableViewController.h"

@interface SMCoreDataTableViewController ()
@property (nonatomic) BOOL beganUpdates;
@end

@implementation SMCoreDataTableViewController

- (void)performFetch

    if (self.fetchedResultsController) 
        if (self.fetchedResultsController.fetchRequest.predicate) 
            if (self.debug) NSLog(@"[%@ %@] fetching %@ with predicate: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName, self.fetchedResultsController.fetchRequest.predicate);
         else 
            if (self.debug) NSLog(@"[%@ %@] fetching all %@ (i.e., no predicate)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName);
        
        NSError *error;
        [self.fetchedResultsController performFetch:&error];
        if (error) NSLog(@"[%@ %@] %@ (%@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [error localizedDescription], [error localizedFailureReason]);
     else 
        if (self.debug) NSLog(@"[%@ %@] no NSFetchedResultsController (yet?)", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    
    [self.tableView reloadData];


- (void)setupFetchedResultsController:(NSFetchedResultsController *)newfrc

    NSFetchedResultsController *oldfrc = _fetchedResultsController;
    if (newfrc != oldfrc) 
        _fetchedResultsController = newfrc;
        newfrc.delegate = self;
        if ((!self.title || [self.title isEqualToString:oldfrc.fetchRequest.entity.name]) && (!self.navigationController || !self.navigationItem.title)) 
            self.title = newfrc.fetchRequest.entity.name;
        
        if (newfrc) 
            if (self.debug) NSLog(@"[%@ %@] %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), oldfrc ? @"updated" : @"set");
            [self performFetch];
         else 
            if (self.debug) NSLog(@"[%@ %@] reset to nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
            [self.tableView reloadData];
        
    


#pragma mark - UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

    return [[self.fetchedResultsController sections] count];


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

    return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];


- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section

    return [[[self.fetchedResultsController sections] objectAtIndex:section] name];


- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index

    return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];


- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView

    return [self.fetchedResultsController sectionIndexTitles];


#pragma mark - NSFetchedResultsControllerDelegate

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller

    if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext) 
        [self.tableView beginUpdates];
        self.beganUpdates = YES;
    


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

    if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
    
        switch(type)
        
            case NSFetchedResultsChangeInsert:
                [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
                break;

            case NSFetchedResultsChangeDelete:
                [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

    if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
    
        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.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;

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


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller

    if (self.beganUpdates) [self.tableView endUpdates];


- (void)endSuspensionOfUpdatesDueToContextChanges

    _suspendAutomaticTrackingOfChangesInManagedObjectContext = NO;


- (void)setSuspendAutomaticTrackingOfChangesInManagedObjectContext:(BOOL)suspend

    if (suspend) 
        _suspendAutomaticTrackingOfChangesInManagedObjectContext = YES;
     else 
        [self performSelector:@selector(endSuspensionOfUpdatesDueToContextChanges) withObject:0 afterDelay:0];
    


@end

SMCoreDataTableViewController.h:

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>

@interface SMCoreDataTableViewController : UITableViewController <NSFetchedResultsControllerDelegate>

@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;

@property (nonatomic) BOOL suspendAutomaticTrackingOfChangesInManagedObjectContext;
@property BOOL debug;

- (void)setupFetchedResultsController:(NSFetchedResultsController *)fetchedResultsController;

@end

【讨论】:

以上是关于在使用 NSFetchedResultsController 更新 UITableView 时将来自 Web 的数据保存到 Core Data的主要内容,如果未能解决你的问题,请参考以下文章

NSFetchedresultscontroller 与 tableview 插入新部分崩溃

更新相关 NSManagedObject 时更新 UITableView

Core Data 中计算的持久属性

NSFetchedResultsController:具有相同键的两个排序描述符不起作用。一个降序用于部分,另一个升序用于行

在 spark 上使用集群和在本地使用并行操作有啥区别?

在哪里使用 CORBA 以及在哪里使用 SNMP 进行监控?