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

发布于 2024-08-30 08:54:26 字数 2405 浏览 8 评论 0原文

我利用 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"];

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

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

忆沫 2024-09-06 08:54:27

我通过 Ray Wenderlich 论坛发现

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

所以,在我的例子中发生的事情是这样的:

  1. 正常的启动过程需要构建一个 NSFetchedResultsController。
  2. 由于正在获取数千个对象,因此新的获取需要相当长的时间。为了缓解这种情况,获取 a) 使用缓存,b) 在后台进行,允许其他活动继续
  3. 正常情况下,尽管 UI 具有响应能力并且用户可以执行某些操作,但获取早在用户可能表达 a 之前就已完成渴望创建一个新的对象。
  4. 但是,有时应用程序将在后台启动 - 例如,从 WatchKit 事件或后台获取等) - 并且启动的一部分将需要立即在数据存储中创建一个新对象。
  5. 如果在获取完成之前创建新对象,应用程序将会崩溃。

解决方案是确保在创建对象之前提取已完成(这会影响提取)。

或者,您可以删除缓存,但实际上性能较差。

请注意,调试器发出的警告

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

根本无法捕获这种情况,因为您更改的不是请求、谓词或排序描述符,但更准确地说,可以描述为在获取过程中改变了结果集

我花了很长时间才找到这个小小的琐事。我希望你受益。

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.

递刀给你 2024-09-06 08:54:27
NSFetchedResultsController *fetched = [[NSFetchedResultsController alloc] initWithFetchRequest:fetch managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"day" cacheName:@"Events"];

将 @"Events" 替换为 nil。

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

replace @"Events" with nil.

前事休说 2024-09-06 08:54:26

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

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

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

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.

乜一 2024-09-06 08:54:26

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

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

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

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

[NSFetchedResultsController deleteCacheWithName:nil];

突然,我只是为了熟悉 CoreData 而开发的小应用程序又开始工作了。

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.

少女七分熟 2024-09-06 08:54:26

直接来自 NSFetchedResultsController

修改获取请求

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

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

  2. 更改获取请求。

  3. 调用performFetch:

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

阳光①夏 2024-09-06 08:54:26

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

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

我想你必须评估你是否需要缓存。指定 nil 意味着每次调用都会进行一次提取。

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

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.

清欢 2024-09-06 08:54:26

如果您正在使用模拟器,请尝试重置它 - 我猜您已经更改了实体映射,并且它被剩余的缓存弄糊涂了。如果没有,您可以尝试按照错误提示执行操作:

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

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
}
鱼窥荷 2024-09-06 08:54:26

Xcode 7(beta 4)仍然出现异常:

您非法改变了 NSFetchedResultsController 的获取
请求、其谓词或其排序描述符,不带任何一个
禁用缓存或使用 +deleteCacheWithName:

注意:这是使用未修改的 Xcode“模板”Master-Detail iOS 应用以及标准 Xcode CoreData“boiler-plate”< /em> 使用 Xcode 7 创建并使用最新 (iOS 9) 部署目标的代码。

当我在模拟器中重新启动我的应用程序时,我首先注意到了这一点。我已经通过 Xcode 多次启动和停止该应用程序,然后就发生了;这种事一直在发生。我决定做一些实验,结果是:

  • 每次我在模拟器中停止应用程序时,我都会在后续启动时遇到异常。
  • 每次我使用模拟器的Home按钮停止应用程序时,我都能成功地再次启动它。

使用以下方法中的其中一个仍然可以按如下方式修复

  • 在 AppDelegate 的 application didFinishLaunchingWithOptions 方法中,添加以下内容Swift NSFetchedResultsController.deleteCacheWithName(nil) 或 Objective-C [NSFetchedResultsController deleteCacheWithName:nil]; 代码。这会清除损坏的缓存。
  • 在模拟器中,从模拟器菜单中选择重置内容和设置。这解决了问题,但您丢失了测试数据

我还相信这是通过 Xcode 运行并在应用程序能够清理之前停止应用程序的产物。我在实际设备中没有看到这一点。

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.

时光与爱终年不遇 2024-09-06 08:54:26

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

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.

滴情不沾 2024-09-06 08:54:26

有多少个类实现相同的 - (NSFetchedResultsController *)results 方法,您是否为每个类使用不同的缓存?我遇到了同样的问题,我想我可以通过在某些类之前使用不同的缓存名称来修复它,因为我有不同的 NSPredicates。

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.

回首观望 2024-09-06 08:54:26

我也遇到了同样的问题。
为了解决这个问题,我将 [NSFetchedResultsController deleteCacheWithName:@"cacheName"]; 放在 resultsController 的 init 之前。对我有用,因为他只第一次去那里。

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.

红颜悴 2024-09-06 08:54:26

对于现在遇到同样问题的人来说,问题是 Core Data 不清理缓存,所以第一次工作正常,但之后就不行了。然后只需在初始化 NSFetchRequest 之后放置此行

[NSFetchedResultsController deleteCacheWithName:@"Name"];

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"];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文