NSFetchedResultsController在performFetch上崩溃:使用缓存时

时间:2010-04-25 20:09:11

标签: iphone objective-c core-data nsfetchedresultscontroller

我使用NSFetchedResultsController来显示一堆对象,这些对象使用日期进行分区。在全新安装中,它完全正常工作,对象显示在表格视图中。然而,似乎当应用程序重新启动时,我遇到了崩溃。我在初始化NSFetchedResultsController时指定了一个缓存,当我不这样做时,它完美地工作。

以下是我创建NSFetchedResultsController的方法:

- (NSFetchedResultsController *)results {
    // If we are not nil, stop here
    if (results != nil)
        return results;

    // Create the fetch request, entity and sort descriptors
    NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:self.managedObjectContext];
    NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"utc_start" ascending:YES];
    NSArray *descriptors = [[NSArray alloc] initWithObjects:descriptor, nil];

    // Set properties on the fetch
    [fetch setEntity:entity];
    [fetch setSortDescriptors:descriptors];

    // Create a fresh fetched results controller
    NSFetchedResultsController *fetched = [[NSFetchedResultsController alloc] initWithFetchRequest:fetch managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"day" cacheName:@"Events"];
    fetched.delegate = self;
    self.results = fetched;

    // Release objects and return our controller
    [fetched release];
    [fetch release];
    [descriptor release];
    [descriptors release];
    return results;
}

以下是应用崩溃时收到的消息:

FATAL ERROR: The persistent cache of section information does not match the current configuration.  You have illegally mutated the NSFetchedResultsController's fetch request, its predicate, or its sort descriptor without either disabling caching or using +deleteCacheWithName:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'FATAL ERROR: The persistent cache of section information does not match the current configuration.  You have illegally mutated the NSFetchedResultsController's fetch request, its predicate, or its sort descriptor without either disabling caching or using +deleteCacheWithName:'

我真的不知道为什么会这样说,因为我不相信我会做任何会造成这种情况的特殊事情。唯一可能的问题是节标题(日期),我在创建新对象时将其构造成这样:

// Set the new format
[formatter setDateFormat:@"dd MMMM"];

// Set the day of the event
[event setValue:[formatter stringFromDate:[event valueForKey:@"utc_start"]] forKey:@"day"];

就像我提到的,如果没有涉及缓存,所有这些都可以正常工作。任何帮助表示赞赏!

12 个答案:

答案 0 :(得分:65)

当Apple发布新的iOS 4.0时,我的一个应用程序遇到了类似的问题。 搜索:

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

并将参数cacheName的值设置为nil。它对我有用,希望它能为你服务。让我知道。

答案 1 :(得分:30)

当我通过MacBook Pro升级到Snow Leopard 10.6.4和最新的SDK时,我开始遇到同样的错误。

事实证明,我们中的许多人一直在使用不符合规则的代码,但我们不知道它,因为CoreData并没有按照自己的规则行事。

具体来说,当您获取内容时,它们会被缓存,而在4.0中,如果在早期SDK中清除该缓存,则不会自动清除该缓存。

对我来说,解决方案很简单。我刚刚使用了清除缓存的类方法。你可以指定一个单独的实体,但是我指定了nil,所以它只是在这个特定的启动代码中完成它们:

[NSFetchedResultsController deleteCacheWithName:nil];

突然之间,我所做的只是让我自己熟悉CoreData的小应用程序又重新开始了。

答案 2 :(得分:15)

直接来自NSFetchedResultsController的文档:

  

修改获取请求

     

您不能简单地更改获取请求   修改结果。如果您想更改获取请求,那么您   必须:

     
      
  1. 如果您使用的是缓存,请将其删除(使用 deleteCacheWithName: )。   通常,如果要更改提取,则不应使用缓存   请求。

  2.   
  3. 更改提取请求。

  4.   
  5. 调用 performFetch:

  6.   

答案 3 :(得分:6)

我遇到了类似的问题。 当我检查调试器控制台时,它显示了缓存对象和获取的对象是什么,以便我可以找出它们不一致的原因。 在我的情况下,这是由于不同的谓词。

由于我的谓词中的值不是动态的,我可以为每个谓词指定不同的缓存名称。这将为我指定的每个“类型”创建一个缓存。

我想您必须评估是否需要缓存。要指定nil,表示在每次调用中都进行提取。

我发现只有当获取请求有一些更改时才会发生错误。如果要创建新的NSFetchRequest或更改谓词OR排序描述符,则应删除缓存或使用其他缓存。否则,请确保您具有相同的NSFetchRequest或确保保留NSFetchedResultsController,这应该可以解决您的问题。

答案 4 :(得分:3)

如果您正在使用模拟器,请尝试重置它 - 我猜您已经更改了实体地图,并且它被剩余的缓存搞糊涂了。如果没有,你可以尝试做错误所说的:

- (void)applicationWillTerminate:(UIApplication *)application {
    [NSFetchedResultsController deleteCacheNamed:@"Events"];
    //etc
}

答案 5 :(得分:3)

The exception still occurs with Xcode 7 (beta 4):

You have illegally mutated the NSFetchedResultsController's fetch request, its predicate, or its sort descriptor without either disabling caching or using +deleteCacheWithName:

NOTE: This is with an unmodified Xcode "Template" Master-Detail iOS app with standard Xcode CoreData "boiler-plate" code created with Xcode 7 and using the latest (iOS 9) deployment target.

I noticed it first when I restarted my app in the simulator. I had been starting and stopping the app several times via Xcode and then it happened; and it kept happening. I decided to do some experiments, and the results:

  • Every time I stopped the app in the simulator, I would get the exception on a subsequent launch.
  • Every time I stopped the app using the simulator's Home button, I was able to launch it again successfully.

The issue can still be fixed as follows, using one or the other of the following methods:

  • In the AppDelegate's application didFinishLaunchingWithOptions method, add the following Swift NSFetchedResultsController.deleteCacheWithName(nil) or Objective-C [NSFetchedResultsController deleteCacheWithName:nil]; code. This clears out the corrupted cache.
  • In the Simulator, from the Simulator menu, select Reset Content and Settings. This fixes the problem, but you lose your test data.

I also believe that this is an artifact of running via Xcode and stopping the app prior to it being able to clean up. I've not seen this in the actual device.

答案 6 :(得分:1)

您是使用主页按钮退出模拟器还是在Xcode中终止应用程序?也许应用程序没有时间完成写入缓存。尝试使用主页按钮退出应用程序。

答案 7 :(得分:1)

有多少个类实现了相同的 - (NSFetchedResultsController *)结果方法,你为每个类使用不同的缓存吗?我遇到了同样的问题,我想我通过使用不同的缓存名称修复它,因为我有不同的NSPredicates。

答案 8 :(得分:1)

我遇到了同样的问题。
要修复,我将[NSFetchedResultsController deleteCacheWithName:@"cacheName"];放在resultController的init之前。适合我,因为他第一次去那里。

答案 9 :(得分:1)

对于那些现在遇到同样问题的人来说,问题在于Core Data不会清理缓存,所以第一次工作正常,但之后却没有。然后在init NSFetchRequest

之后立即放置这一行
[NSFetchedResultsController deleteCacheWithName:@"Name"];

答案 10 :(得分:1)

我通过Ray Wenderlich forums找到了

  

请注意,只有当您向数据存储区添加新内容时尚未创建获取请求时,它才会崩溃,即尚未加载位置视图时。如果已经加载了视图,那么它可以正常工作。奇怪,嗯?

所以,我的案子发生了什么:

  1. 正常启动过程需要构建NSFetchedResultsController。
  2. 因为有数千个对象被提取,所以新鲜的提取需要相当长的时间。为了缓解这种情况,获取a)使用缓存,b)在后台发生,允许其他活动继续
  3. 通常情况下,虽然用户界面是响应式的并且用户可以执行操作,但在用户表达创建新对象的愿望之前很久就完成了提取。
  4. 但是,有时应用程序将在后台启动 - 例如,从WatchKit事件或后台获取等) - 并且部分启动将需要立即在数据存储区中创建新对象。
  5. 如果在获取完成之前创建了新对象,则应用程序将崩溃。
  6. 解决方案是确保在创建对象之前完成提取(这将影响提取)。

    或者你可以删除缓存,但实际上这种性能不太好。

    请注意来自调试器的警告

      

    你非法改变了NSFetchedResultsController的获取请求,它的谓词或它的排序描述符,而没有禁用缓存或使用+ deleteCacheWithName:

    根本不会捕获这种情况,因为你所改变的不是请求,谓词或排序描述符,而是更准确地说可以描述为在获取进行过程中突变了结果集

    我花了很长时间才追查这一小块琐事。我希望你受益。

答案 11 :(得分:-2)

NSFetchedResultsController *fetched = [[NSFetchedResultsController alloc] initWithFetchRequest:fetch managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"day" cacheName:@"Events"];

用nil替换@“Events”。