【问题标题】:UITableView with NSFetchedResultsController Does Not Load the Second Time带有 NSFetchedResultsController 的 UITableView 不会第二次加载
【发布时间】:2013-01-18 13:58:15
【问题描述】:

Update 3这些是第一次运行后的日志,数据存储为空。

2013-02-07 20:57:06.708 Five Hundred Things[14763:c07] mainMOC = <NSManagedObjectContext: 0x7475a90>
2013-02-07 20:57:06.711 Five Hundred Things[14763:1303] Import started
2013-02-07 20:57:06.712 Five Hundred Things[14763:1303] backgroundMOC = <NSManagedObjectContext: 0x8570070>
2013-02-07 20:57:06.717 Five Hundred Things[14763:c07] FRC fetch performed
2013-02-07 20:57:06.718 Five Hundred Things[14763:c07] numberOfSectionsInTableView returns 1
2013-02-07 20:57:06.720 Five Hundred Things[14763:c07] numberOfSectionsInTableView returns 1
2013-02-07 20:57:06.720 Five Hundred Things[14763:c07] numberOfRowsInSection returns 0
2013-02-07 20:57:06.728 Five Hundred Things[14763:1303] call contextDidSave
2013-02-07 20:57:06.736 Five Hundred Things[14763:1303] call contextDidSave
2013-02-07 20:57:06.736 Five Hundred Things[14763:c07] numberOfSectionsInTableView returns 1
2013-02-07 20:57:06.737 Five Hundred Things[14763:c07] numberOfSectionsInTableView returns 1
2013-02-07 20:57:06.737 Five Hundred Things[14763:c07] numberOfRowsInSection returns 5
2013-02-07 20:57:06.758 Five Hundred Things[14763:1303] call contextDidSave
2013-02-07 20:57:06.759 Five Hundred Things[14763:1303] Refresh complete
2013-02-07 20:57:06.759 Five Hundred Things[14763:c07] numberOfSectionsInTableView returns 1
2013-02-07 20:57:06.760 Five Hundred Things[14763:c07] numberOfSectionsInTableView returns 1
2013-02-07 20:57:06.761 Five Hundred Things[14763:c07] numberOfRowsInSection returns 5

注意执行FRC fetch,section中的行数为0,但是在第二个contextDidSave之后,它变为5以匹配数据存储中的类别数。

在崩溃的第二次运行中,以下是日志:

2013-02-07 21:01:11.578 Five Hundred Things[14800:c07] mainMOC = <NSManagedObjectContext: 0x8225650>
2013-02-07 21:01:11.581 Five Hundred Things[14800:1303] Import started
2013-02-07 21:01:11.582 Five Hundred Things[14800:1303] backgroundMOC = <NSManagedObjectContext: 0x7439850>
2013-02-07 21:01:11.592 Five Hundred Things[14800:c07] FRC fetch performed
2013-02-07 21:01:11.594 Five Hundred Things[14800:c07] cat = Attraction
2013-02-07 21:01:11.594 Five Hundred Things[14800:c07] cat = Beverage
2013-02-07 21:01:11.595 Five Hundred Things[14800:c07] cat = Entertainment
2013-02-07 21:01:11.595 Five Hundred Things[14800:c07] cat = Hotel
2013-02-07 21:01:11.596 Five Hundred Things[14800:c07] cat = Restaurant
2013-02-07 21:01:11.597 Five Hundred Things[14800:c07] numberOfSectionsInTableView returns 1
2013-02-07 21:01:11.598 Five Hundred Things[14800:c07] numberOfSectionsInTableView returns 1
2013-02-07 21:01:11.599 Five Hundred Things[14800:c07] numberOfRowsInSection returns 0
2013-02-07 21:01:11.602 Five Hundred Things[14800:1303] call contextDidSave
2013-02-07 21:01:11.610 Five Hundred Things[14800:1303] call contextDidSave

FRC 被初始化,然后立即记录类别以显示它们确实在 FRC 中。但是,该部分中的行数为 0,并且永远不会更新。相反,应用程序会因下面的堆栈而崩溃。

在第三次及后续运行中,日志如下所示:

2013-02-07 21:03:55.560 Five Hundred Things[14815:c07] mainMOC = <NSManagedObjectContext: 0x8128860>
2013-02-07 21:03:55.563 Five Hundred Things[14815:1e03] Import started
2013-02-07 21:03:55.564 Five Hundred Things[14815:1e03] backgroundMOC = <NSManagedObjectContext: 0x822b5d0>
2013-02-07 21:03:55.569 Five Hundred Things[14815:c07] FRC fetch performed
2013-02-07 21:03:55.571 Five Hundred Things[14815:c07] cat = Attraction
2013-02-07 21:03:55.572 Five Hundred Things[14815:c07] cat = Beverage
2013-02-07 21:03:55.572 Five Hundred Things[14815:c07] cat = Entertainment
2013-02-07 21:03:55.573 Five Hundred Things[14815:c07] cat = Hotel
2013-02-07 21:03:55.573 Five Hundred Things[14815:c07] cat = Restaurant
2013-02-07 21:03:55.574 Five Hundred Things[14815:c07] numberOfSectionsInTableView returns 1
2013-02-07 21:03:55.576 Five Hundred Things[14815:c07] numberOfSectionsInTableView returns 1
2013-02-07 21:03:55.576 Five Hundred Things[14815:c07] numberOfRowsInSection returns 5
2013-02-07 21:03:55.581 Five Hundred Things[14815:1e03] call contextDidSave
2013-02-07 21:03:55.592 Five Hundred Things[14815:1e03] call contextDidSave
2013-02-07 21:03:55.593 Five Hundred Things[14815:c07] numberOfSectionsInTableView returns 1
2013-02-07 21:03:55.594 Five Hundred Things[14815:c07] numberOfSectionsInTableView returns 1
2013-02-07 21:03:55.595 Five Hundred Things[14815:c07] numberOfRowsInSection returns 5
2013-02-07 21:03:55.606 Five Hundred Things[14815:1e03] call contextDidSave
2013-02-07 21:03:55.606 Five Hundred Things[14815:1e03] Refresh complete

这是该行为在第二次运行时的样子;数据已经在 store 中,section 中的行数返回 5,并且类别立即出现在 table view 中。


更新 2 这是主线程的堆栈跟踪,这是发生崩溃的地方。由于它发生在主线程上,我认为它与 UITableView 有关。我没有在 UITableView 中使用 NSDictionary 或 NSMutableDictionary。我现在的想法是 numberOfRowsInSection 在第二次运行时返回 0 导致了这个问题,但我不知道如何解决它。它在第三次运行时返回正确的数字(5 和我正在使用的数据),并且似乎在第一次运行时正确填充了数据存储,所以我很困惑为什么在第二次运行时它返回 0 而没有不更新。

frame #0: 0x013ede52 libobjc.A.dylib`objc_exception_throw
frame #1: 0x020330de CoreFoundation`-[__NSDictionaryM setObject:forKey:] + 158
frame #2: 0x01211d7a CoreData`-[NSFetchedResultsController(PrivateMethods) _preprocessUpdatedObjects:insertsInfo:deletesInfo:updatesInfo:sectionsWithDeletes:newSectionNames:treatAsRefreshes:] + 1994
frame #3: 0x01212ed7 CoreData`-[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 2455
frame #4: 0x00b9e4f9 Foundation`__57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 + 40
frame #5: 0x0200a0c5 CoreFoundation`___CFXNotificationPost_block_invoke_0 + 85
frame #6: 0x01f64efa CoreFoundation`_CFXNotificationPost + 2122
frame #7: 0x00ad2bb2 Foundation`-[NSNotificationCenter postNotificationName:object:userInfo:] + 98
frame #8: 0x01125163 CoreData`-[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 83
frame #9: 0x011bed2f CoreData`-[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:] + 367
frame #10: 0x01121128 CoreData`-[NSManagedObjectContext(_NSInternalChangeProcessing) _postRefreshedObjectsNotificationAndClearList] + 136
frame #11: 0x0111f8c0 CoreData`-[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 80
frame #12: 0x0111f869 CoreData`-[NSManagedObjectContext processPendingChanges] + 41
frame #13: 0x010f3e38 CoreData`_performRunLoopAction + 280
frame #14: 0x01f78afe CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
frame #15: 0x01f78a3d CoreFoundation`__CFRunLoopDoObservers + 381
frame #16: 0x01f567c2 CoreFoundation`__CFRunLoopRun + 1106
frame #17: 0x01f55f44 CoreFoundation`CFRunLoopRunSpecific + 276
frame #18: 0x01f55e1b CoreFoundation`CFRunLoopRunInMode + 123
frame #19: 0x01f0a7e3 GraphicsServices`GSEventRunModal + 88
frame #20: 0x01f0a668 GraphicsServices`GSEventRun + 104
frame #21: 0x00021ffc UIKit`UIApplicationMain + 1211
frame #22: 0x000022dd Five Hundred Things`main(argc=1, argv=0xbffff31c) + 141 at main.m:16
frame #23: 0x00002205 Five Hundred Things`start + 53

更新:我设法得到了实际的崩溃,而不是没有响应。

* 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“* setObjectForKey: object cannot be nil (key: _ContentChange_OldIndexPathKey)”

This SO question 是我能找到的最接近错误的,但它讨论的是 nil 而不是键的值。它看起来像是在将类别保存到核心数据存储时发生的,但所有类别都有值。

实体类别包含 category_id - 整数 16 类别名称 - 字符串

它与实体 Thing 有一对多的关系,但代码的这个特定部分对这种关系没有任何作用;它只是设置 category_id 和 category_name。在导入的后期(相关的 MOC 保存之后)是设置关系的时候。

导入操作中有问题的代码:

//import categories

    NSString *categoryPath = [[NSBundle mainBundle] pathForResource:@"category" ofType:@"json"];

    NSData *categoryData = [NSData dataWithContentsOfFile:categoryPath];

    NSDictionary *categoryResults = [NSJSONSerialization
                                     JSONObjectWithData:categoryData
                                     options:NSJSONReadingMutableLeaves
                                     error:&error];

    NSEntityDescription *categoryEntity = [NSEntityDescription entityForName:@"Category"
                                              inManagedObjectContext:context];

    NSMutableArray *categories = [[NSMutableArray alloc] init];

    NSString *categoryPredicateString = [NSString stringWithFormat: @"category_id == $CATEGORY_ID"];

    NSPredicate *categoryPredicate = [NSPredicate predicateWithFormat:categoryPredicateString];


    for (NSDictionary *categoryKey in categoryResults){

        NSFetchRequest *categoryFetchRequest = [[NSFetchRequest alloc] init];

        [categoryFetchRequest setEntity:categoryEntity];

        NSNumber *categoryID = [NSNumber numberWithInt:[[categoryKey objectForKey:@"category_id"] integerValue]];

        [categories addObject:categoryID];

        NSDictionary *categoryVariables = [NSDictionary dictionaryWithObject:categoryID forKey:@"CATEGORY_ID"];

        NSPredicate *catSubPredicate = [categoryPredicate predicateWithSubstitutionVariables:categoryVariables];

        [categoryFetchRequest setPredicate:catSubPredicate];

        NSArray *categoryArray = [[NSArray alloc] init];
        categoryArray = [context executeFetchRequest:categoryFetchRequest error:&error];

        Category *categoryObject = [categoryArray lastObject];
        NSNumber *categoryNum = [categoryObject valueForKey:@"category_id"];
        NSInteger categoryInt = [categoryNum integerValue];

        if (categoryInt != [[categoryKey objectForKey:@"category_id"] integerValue]){
            categoryObject = [NSEntityDescription
                              insertNewObjectForEntityForName:@"Category"
                              inManagedObjectContext:context];
            categoryObject.category_id = [NSNumber numberWithInt:[[categoryKey objectForKey:@"category_id"] intValue]];
        }
        if (categoryObject.category_name != [categoryKey objectForKey:@"category"]){
            categoryObject.category_name = [categoryKey objectForKey:@"category"];
        }

    }

    //Remove unneeded Categories from Core Data Store

    NSFetchRequest *removeUnusedCategories = [[NSFetchRequest alloc] init];
    [removeUnusedCategories setEntity:categoryEntity];
    NSArray *fetchedCategories = [context executeFetchRequest:removeUnusedCategories error:&error];

    for (Category *fetchedCategory in fetchedCategories){
        if (![categories containsObject:fetchedCategory.category_id]){
            [context deleteObject:fetchedCategory];
            NSLog(@"Object deleted");
        }
    }

    if (![context save:&error]) {
        NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
    }

[context save] 出现在后台 MOC 上,并通过通知中心同步到主 MOC(在应用代理中)。它监听NSManagedObjectContextDidSaveNotification 并在主MOC 上运行mergeChangesFromContextDidSaveNotification:

第一次运行和第三次运行完美。它总是在第二次运行时发生。


我在一个 iOS 项目上使用 Core Data,到目前为止它运行良好,除了一个问题。

应用程序从 JSON 文件填充 Core Data 存储,并且初始 UITableViewController 加载了应有的动画。但是,应用程序第二次启动时,初始 UITableView 是空白的。我检查了多个地方,第二次启动时数据在 Core Data 存储中,但没有调用任何 UITableView 或 NSFetchedResultsController 方法。

在第一次启动时,section 中的行数返回 0,但在加载 Core Data 存储后返回 5。在第二次启动时,节中的行数(只有一节)返回 0 并且不更新。在第三次和所有后续启动时,该部分中的行数将返回 5。

UITableView 的 cellForRowAtIndexPath 和 NSFetchedResultsController 的 didChangeObject 方法都不会在应用的第二次启动时被调用。 UITableViewController 是UITableViewDelegateUITableViewDataSourceNSFetchedResultsControllerDelegate

正如核心数据指南中所建议的,应用程序委托和表视图控制器共享一个托管对象上下文,而数据加载是在另一个线程中的后台 MOC 上完成的。当通过mergeChangesFromContextDidSaveNotification: 调用上下文的保存方法时,这些正在同步。

为了重现,我从模拟器中删除了应用程序,运行一次,数据库填充并且应用程序正确显示。我停止应用程序并再次运行,但没有任何显示。我停止该应用程序并第三次运行它并正确显示。

除了第二次启动应用程序之外,所有这些似乎都可以正常工作。第一次和第三次工作正常。我错过了什么?

至于我的代码,我不知道该放什么。让我们从 UITableViewController 的实现开始。

@implementation FTWTMasterViewController

@synthesize managedObjectContext;
@synthesize categoryController = _categoryController;
@synthesize catLocViewController;

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

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

    NSLog(@"tableview MOC = %@", self.managedObjectContext);

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Category" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    NSSortDescriptor *sort = [[NSSortDescriptor alloc]
                              initWithKey:@"category_name" ascending:YES];
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];

    [fetchRequest setFetchBatchSize:20];

    NSFetchedResultsController *theFetchedResultsController =
    [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                        managedObjectContext:self.managedObjectContext
                                          sectionNameKeyPath:nil
                                                   cacheName:@"CategoryTable"];
    _categoryController = theFetchedResultsController;
    _categoryController.delegate = self;

    return _categoryController;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.tableView.dataSource = self;
    self.tableView.delegate = self;

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

    NSLog(@"Fetch called");

    self.title = @"Categories";
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    NSLog(@"number of sections = %lu", (unsigned long)[[self.categoryController sections] count]);
    return [[self.categoryController sections] count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    id  sectionInfo =
        [[_categoryController sections] objectAtIndex:section];

    NSLog(@"numberOfObjects = %lu", (unsigned long)[sectionInfo numberOfObjects]);
    return [sectionInfo numberOfObjects];
}

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    Category *category = [_categoryController objectAtIndexPath:indexPath];
    cell.textLabel.text = category.category_name;
    NSLog(@"config cell %@", category.category_name);
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"tableView setup");
    static NSString *CellIdentifier = @"categoryCell";

    UITableViewCell *cell =
    [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

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

    return cell;
}

#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    Category *aCategory = [self.categoryController objectAtIndexPath:indexPath];

    if (self.catLocViewController == nil){
        FTWTCatLocationViewController *aCatLocController = [[FTWTCatLocationViewController alloc] init];
        self.catLocViewController = aCatLocController;
    }

    self.catLocViewController.selectedCat = aCategory;
    aCategory = nil;

    self.catLocViewController.managedObjectContext = self.managedObjectContext;

    [self.navigationController pushViewController:self.catLocViewController animated:YES];

    self.catLocViewController = nil;
}

#pragma mark - Fetched results controller delegate
- (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 didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

    NSLog(@"didChangeObject");

    UITableView *tableView = self.tableView;

    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];
            break;

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


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

    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)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    // The fetch controller has sent all current change notifications, so tell the table view to process all updates.
    [self.tableView endUpdates];
}

@end

【问题讨论】:

  • 你有没有在 fetched results 控制器上调用 performFetch: ?第一次加载时是否保存托管对象上下文?
  • performFetch:viewDidLoad 中被调用(如上所示),并在导入每个 JSON 文件后保存 MOC。
  • 每次启动应用程序时是否都会调用 viewDidLoad(尤其是第二次检查)。我想知道这是因为在您的 numberOfRowsInSection 中,您访问的是 categoryController 的实际实例变量而不是属性。因此,这不会为此调用 getter,并且可能无法通过延迟实例化正确设置它。您是否尝试过在 numberOfRows... 中使用 self.categoryController ?只是一个初步的想法。
  • 当您在 Xcode 中按停止时,据我所知,这与在 iOS 托盘中杀死应用程序相同。每次您在 Xcode 中运行应用程序时,它都会自行重新启动,而不是进入非活动状态。这是最初的视图控制器,所以当应用程序启动时它是第一个被调用的。我只是尝试输入self.categoryController 而不是_categoryController,从模拟器中清除了应用程序,然后运行了两次,但在第二次运行时崩溃并出现相同的错误和堆栈。
  • @scotoliver:我可以重现您的问题,我认为这是 Apple 的错误。缓存只用于缓存section信息,所以指定没有sectionNameKeyPath的缓存是没有意义的。但是指定没有节的缓存不应导致这种不稳定的行为。似乎在第一次运行时缓存了某些内容(当表仍然为空时),导致第二次运行时显示错误。然后在第二次运行时重新创建缓存,以便在第三次运行时一切正常。

标签: ios objective-c uitableview core-data nsfetchedresultscontroller


【解决方案1】:

与我在上面评论中的假设相反,如果指定 sectionNameKeyPath 来创建表格视图部分,也会发生相同的效果(我可以用 测试程序)。

当应用程序第一次启动时,会发生以下情况:

  1. 创建了一个持久性存储文件“appname.sqlite”。
  2. 使用cacheName 参数集创建表视图的获取结果控制器,以便创建节缓存文件。此时,所有部分都是空的。
  3. 创建了一个后台 MOC,它从资源文件中读取一些 JSON 数据并将对象添加到上下文中。
  4. 后台 MOC 已保存。

(顺便说一句,缓存文件是

   Library/Caches/<bundle-id>/.CoreDataCaches/SectionInfoCaches/<tablename>/sectionInfo

在应用程序包中。)

当应用第二次启动时,获取的结果控制器会检查部分信息缓存是否仍然有效或必须重新创建。根据文档,它比较了持久存储文件和部分缓存文件的修改时间。

现在有趣的部分:如果(在第一次运行中)存储文件的创建(第 1 步)和保存更新的上下文(第 4 步)发生在同一秒,那么修改第 4 步中存储文件的日期没有更改!!

因此,部分缓存文件仍被视为有效且未重新创建。由于所有部分都是空的(在步骤 2 中),FRC 使用此缓存信息并仅显示空部分。

后台 MOC 再次启动并保存上下文。现在商店文件有一个新的修改日期,因此在应用程序的第三次运行时,部分和行将正确显示。

为了确认我的“理论”,我在第一次和第二次运行之间手动“触摸”了存储文件,以强制更改修改日期。然后所有部分和行都正确显示。

(我只在 iPhone Simulator 中测试过这个。我不知道 HFS+ 文件系统是否通常具有 1 秒的修改日期分辨率,或者 SQLite 是否在这里做了一些特殊的事情。我稍后会尝试调查。 )

结论:如果存储文件的创建和保存修改数据发生在同一秒内,则可能不会在必要时重新生成节信息缓存文件。

【讨论】:

  • 一定是这种情况,因为我现在正在处理一个小数据集,并且更新发生得非常快。我之前提到的另一个应用程序使用了更大的数据集,其中商店文件的创建和 MOC 保存可能永远不会在同一秒内发生。
  • 在我的情况下删除缓存解决了这个问题..但这有点奇怪。
  • 您可以在应用程序启动期间和创建 NSFetchResultController 之前删除旧缓存。您可以使用类方法 deleteCache(withName:) 删除缓存。
  • 在 WAL 模式(现在是默认模式)下情况更糟,因为 .sqlite-wal 文件是更新的,而被检查的 .sqlite 文件几乎不会更改其时间戳。编辑:发现关于此的错误报告:openradar.appspot.com/17396603
【解决方案2】:

我遇到了同样的问题。当我加载数据时,第一次加载很好。当我重新启动我的应用程序时,数据从表中消失了。这些是我的计数:

self.fetchedResultsController.fetchedObjects: 8
[self.fetchedResultsController.sections count]: 1
[self.fetchedResultsController.sections[0] numberOfObjects]: 0

我有能力在 UI 中更改排序,在更改排序然后再次更改回来后,问题消失了,这肯定指向缓存问题。

打电话

[NSFetchedResultsController deleteCacheWithName:nil];

将删除所有缓存。

我也最终选择暂时不缓存。我不认为我会在我的数据库中为这些表存储数万条记录,所以我不确定缓存是否会有所改善。将cacheName 设置为nil 将阻止对NSFetchedResultsController 的任何缓存。

self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                                    managedObjectContext:self.context
                                                                      sectionNameKeyPath:sectionIdentifier
                                                                               cacheName:nil];

【讨论】:

  • 谢谢!这拯救了我的一天。这也是那个令人讨厌的错误的原因。但最后我删除了所有的缓存。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多