NSFetchedResultsController crashing on performFetch: when using a cache

孤街醉人 提交于 2019-11-28 16:38:15

问题


I make use of NSFetchedResultsController to display a bunch of objects, which are sectioned using dates. On a fresh install, it all works perfectly and the objects are displayed in the table view. However, it seems that when the app is relaunched I get a crash. I specify a cache when initialising the NSFetchedResultsController, and when I don't it works perfectly.

Here is how I create my 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;
}

These are the messages I get when the app crashes:

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:'

I really have no clue as to why it's saying that, as I don't believe I'm doing anything special that would cause this. The only potential issue is the section header (day), which I construct like this when creating a new object:

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

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

Like I mentioned, all of this works fine if there is no cache involved. Any help appreciated!


回答1:


I had a similar problem with one of my apps, when the Apple released the new iOS 4.0. Search:

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

And set the value of the parameter cacheName to nil. It worked for me, hope it will for you. Let me know.




回答2:


I started getting the same error when I upgraded by MacBook Pro to Snow Leopard 10.6.4 and the latest SDK.

As it turns out, many of us had been using code that wasn't in conformance with the rules, but we didn't know it because CoreData wasn't really behaving in accordance with its own rules.

Specifically, when you fetch things, they get cached, and in 4.0, that cache isn't automatically purged in cases where it was purged in the earlier SDK.

For me, the solution was simple. I just employed the class method that purges the caches. You can specify an individual entity, but I specify nil so it just does them all in this particular piece of start-up code:

[NSFetchedResultsController deleteCacheWithName:nil];

Suddenly, the little app I've worked on only to familiarize myself with CoreData is working again.




回答3:


Straight from the documentation for NSFetchedResultsController:

Modifying the Fetch Request

You cannot simply change the fetch request to modify the results. If you want to change the fetch request, you must:

  1. If you are using a cache, delete it (using deleteCacheWithName:). Typically you should not use a cache if you are changing the fetch request.

  2. Change the fetch request.

  3. Invoke performFetch:.




回答4:


I encountered a similar problem. When I inspected the Debugger Console, it showed what the cached objects and the fetched objects were so that I could figure out why they are inconsistent. In my case it was due to a different predicate.

Since the values in my predicate are not dynamic, I could specify a different cache name for each predicate. That will create a cache for each 'type' I specify.

I suppose you will have to assess your need to have the cache. To specify nil, means that a fetch is made in every call.

I figured out that the error occurs only when the fetch request have some changes. If you are creating a new NSFetchRequest OR changing the predicate OR sort descriptor, then you should delete the cache or use a different cache. Otherwise, ensure that you have the same NSFetchRequest or make sure that your NSFetchedResultsController is retained and that should solve your problem.




回答5:


If you're using the simulator, try resetting it--I'd guess you've changed your entity map and it's getting confused by a leftover cache. If not, you could try doing what the error says:

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



回答6:


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.




回答7:


Are you quitting the Simulator using the home button or by terminating the app in Xcode? Maybe the app isn't getting time to finish writing to the cache. Try using the home button to quit the app.




回答8:


How many classes implement the same - (NSFetchedResultsController *)results method, do you use a different cache for each one? I was having the same issue and I think I fix it by using a different cache name fore some clases since I have different NSPredicates.




回答9:


I was having the same problem.
To fix, I put the [NSFetchedResultsController deleteCacheWithName:@"cacheName"]; before the init of resultsController. Works for me as he only goes there on the first time.




回答10:


For those who run into the same problem nowadays, the problem is that somehow Core Data doesn't clean the cache so work fine at first time but doesn't after that. Then just put this line right after init the NSFetchRequest

[NSFetchedResultsController deleteCacheWithName:@"Name"];



回答11:


I found via the Ray Wenderlich forums that

Note that it only crashes when the fetch request is not created yet when you add something new to the datastore, i.e. when the Locations view isn't loaded yet. If the view is loaded already, then it works fine. Odd, eh?

So, what happened in my case was this:

  1. The normal launch process entails constructing an NSFetchedResultsController.
  2. Because there are thousands of objects being fetched, a fresh fetch takes considerable time. To mitigate this, the fetch a) uses a cache, and b) happens in the background, allowing other activities to continue
  3. Normally, though the UI is responsive and the user can do stuff, the fetch is complete long before the user might express a desire to create a new object.
  4. However, sometimes the app will be launched in the background - say, from a WatchKit event, or a background fetch, etc) - and part of the launch will entail creating a new object in the datastore immediately.
  5. If the new object was created before the fetch was complete, the app would crash.

The solution is to ensure that The fetch is complete before creating an object (which would affect the fetch).

Alternatively you could delete the cache, but that is less performant practically speaking.

Note that the admonition from the debugger that

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

simply does not capture this situation whatsoever, in that what you have changed was not the request, predicate, or sort descriptor, but rather more accurately could be described as having mutated the result set while the fetch was in progress.

It took me forever to track down this tiny piece of trivia. I hope you benefit.




回答12:


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

replace @"Events" with nil.



来源:https://stackoverflow.com/questions/2709768/nsfetchedresultscontroller-crashing-on-performfetch-when-using-a-cache

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!