奇怪的核心数据错误

Posted

技术标签:

【中文标题】奇怪的核心数据错误【英文标题】:Weird Core Data Bug 【发布时间】:2010-12-12 18:12:28 【问题描述】:

我的 iPhone (4.1.2) 应用程序有一个菜单,每个条目都指向一个由 NSManagedObject 子类填充的不同表格视图。我对每个视图控制器使用相同的视图控制器,但不同的是获取请求的谓词,因此根据用户选择的菜单项显示不同的数据。很简单。

在我实际保存托管对象上下文之前一切正常。但是,在应用程序进入后台并重新进入前台后,事情变得很奇怪,因为我正在使用这个:

- (void)applicationDidEnterBackground:(UIApplication*)application 
    NSError *error = nil;
    if(managedObjectContext != nil)
        if([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) 
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        

我认为现在是保存数据的合适时机。但是在这发生之后,在我在我的获取结果控制器上调用performFetch: 之后,它的fetchedObjects 是从以前的获取请求中获取的对象,而不是新的。也就是说,如果我这样做:

// Choose menu item 1

[self.fetchedResultsController performFetch:nil]; // With fetch request 1
NSLog(@"%i objects were fetched", [[self.fetchedResultsController fetchedObjects] count]); // Items for fetch request 1

// Go back to menu, choose menu item 2 for new fetch request

[self.fetchedResultsController performFetch:nil]; // Fetch request 2
NSLog(@"%i objects were fetched", [[self.fetchedResultsController fetchedObjects] count]); // Items for Fetch request 2

// Hit home button, applicationDidEnterBackground: is called
// Relaunch
// Choose menu item 1

[self.fetchedResultsController performFetch:nil]; // With fetch request 1
NSLog(@"%i objects were fetched", [[self.fetchedResultsController fetchedObjects] count]); // Items for fetch request 2 - HUH?

所以问题在于在我的托管对象上下文中调用 save:。如果我从不保存数据,那么提取请求永远不会像这样混淆,但是一旦我这样做了,就好像旧的提取请求正在被保存,即使 fetchedResultsController 本身只是在我的视图控制器的 viewDidLoad 方法中创建的。

有人知道发生了什么吗?

更新:更多代码。

- (void)viewDidLoad 
    delegate = (MAAppDelegate *)[[UIApplication sharedApplication] delegate];
    managedObjectContext = delegate.managedObjectContext;

    [self.fetchedResultsController performFetch:nil];
    NSUInteger count = [[self.fetchedResultsController fetchedObjects] count]; // Bug is detected here

    // ...


- (NSFetchedResultsController *)fetchedResultsController 
    if(_fetchedResultsController)
        return _fetchedResultsController;

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Tweet" inManagedObjectContext:managedObjectContext];
    NSPredicate *predicate = nil;
    if([self.navigationItem.title isEqualToString:@"Personal"]) 
        predicate = [NSPredicate predicateWithFormat:@"account.username = %@ AND user.username = %@", delegate.currentAccount.username, delegate.currentAccount.username];
    else if([self.navigationItem.title isEqualToString:@"Favorites"])
        predicate = [NSPredicate predicateWithFormat:@"account.username = %@ AND favorited = YES", delegate.currentAccount.username];
    else
        predicate = [NSPredicate predicateWithFormat:@"account.username = %@", delegate.currentAccount.username];
    [fetchRequest setEntity:entity];
    [fetchRequest setFetchBatchSize:20];
    [fetchRequest setPredicate:predicate];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"tweetID" ascending:NO];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];

    [fetchRequest setSortDescriptors:sortDescriptors];
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                                                                managedObjectContext:managedObjectContext
                                                                                                  sectionNameKeyPath:@"retrieved"
                                                                                                           cacheName:@"Root"];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    [aFetchedResultsController release];
    [fetchRequest release];
    [sortDescriptor release];
    [sortDescriptors release];

    return _fetchedResultsController;

【问题讨论】:

几乎可以肯定,问题不在于将托管对象上下文保存在 applicationDidEnterBackground: 方法中。请发布您的 viewDidLoad 和 fetchedResultsController 方法的代码。 【参考方案1】:

您是否在每个子视图中进行缓存,如果是,您是否为每个缓存使用不同的名称?

【讨论】:

您的 fetchedResults。在您显示的代码 sn-p 中,相关代码是 cacheName:@"Root"]; 每个 FRC 可以有一个缓存名称......如果它们共享一个名称会发生​​奇怪的事情【参考方案2】:

如果不查看其他代码很难判断,但您遇到的问题可能是由于您的 fetchedResultsController 方法中的初始行:

if(_fetchedResultsController)
        return _fetchedResultsController;

正在发生的事情是 - 只是一个猜测 - 您正在重用以前的 _fetchedResultsController,它已使用获取请求 2 的谓词设置。也就是说,在您的 viewDidLoad 方法中

[self.fetchedResultsController performFetch:nil];

并没有像您想象的那样实例化一个新的NSFetchedResultsController:由于最初的两行,在调用fetchedResultsController 时会立即返回前一个。

【讨论】:

以上是关于奇怪的核心数据错误的主要内容,如果未能解决你的问题,请参考以下文章

过度释放导致的奇怪的核心数据错误?

核心数据:使用延迟实例化时的奇怪错误行为

带有谓词的 ios 核心数据错误

使用 NSSortDescriptor 按时间错误排序(带有核心数据)

添加迁移EF Core时出现奇怪错误

核心数据级联删除不可靠?