NSFetchedResultsController找到错误数量的对象 [英] NSFetchedResultsController finds wrong number of objects

查看:51
本文介绍了NSFetchedResultsController找到错误数量的对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看到一种情况,NSFetchRequest返回不同数量的对象,具体取决于它是直接通过NSManagedObjectContext执行还是作为构建NSFetchedResultsController的一部分.

I'm seeing a situation where a NSFetchRequest returns a different number of objects depending on whether it's executed directly through the NSManagedObjectContext or as part of building a NSFetchedResultsController.

示例代码:

- (void)setupResultsController {
    NSError *error = nil;
    NSManagedObjectContext *ctx = [[DataManager sharedInstance] mainObjectContext];

    // Create a fetch request and execute it directly
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    NSEntityDescription *entity = [Song entityInManagedObjectContext:ctx];
    [fetchRequest setEntity:entity];
    [fetchRequest setFetchBatchSize:20];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"section" ascending:YES];
    NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    NSArray *sortDescriptors = @[sortDescriptor, nameDescriptor];

    [fetchRequest setSortDescriptors:sortDescriptors];
    NSArray *debugResults = [ctx executeFetchRequest:fetchRequest error:&error];
    NSLog(@"Count from context fetch: %lu", (unsigned long)debugResults.count);

    // Use the request to populate a NSFetchedResultsController
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
                                                             initWithFetchRequest:fetchRequest
                                                             managedObjectContext:ctx
                                                             sectionNameKeyPath:@"section"
                                                             cacheName:@"Detail"];
    [aFetchedResultsController performFetch:&error];
    NSLog(@"Count from results controller fetch: %lu", (unsigned long)[[aFetchedResultsController fetchedObjects] count]);

    _songResultsController = aFetchedResultsController;
}

执行上述操作会在日志消息中显示

Executing the above results in log messages:

2020-01-10 11:05:07.892772-0500 asb7 [12985:105052]从上下文获取计数:10

2020-01-10 11:05:07.893259-0500 asb7 [12985:105052]从结果控制器访存计数:9

两次获取之间的区别是NSFetchedResultsController缺少最新添加的对象.与此有关的一个极端奇怪之处是,在运行该应用程序一些看似随机的次数之后,计数开始趋于一致,并且获取了新对象.

The difference between the two fetches is that the NSFetchedResultsController is missing the most recently added object. An extreme oddity about this is that, after running the application some seemingly random number of times, the counts start to agree and the new object is fetched.

如果我将 nil 作为缓存名称或删除第二个排序描述符,结果将保持一致.显然,这些会导致不良的行为更改,但可能是线索.

The results become consistent if I pass nil as the cache name or if I remove the second sort descriptor. Obviously these cause undesirable behavior changes but may be clues.

似乎NSFetchedResultsController看到过时的缓存是有效的.更改排序描述符会使缓存无效,但是更新持久存储文件使它无效,但显然在这种情况下不会.

It seems that the NSFetchedResultsController is seeing a stale cache as being valid. Changing a sort descriptor invalidates the cache however updating the persistent store file should invalidate it but apparently does not in this case.

经过更多的实验后,我得到了解释...如果不是解决方案.添加新对象不会更改.sqlite文件的修改日期.它会更新.sqlite-shm和.sqlite-wal,但我猜想在判断是否使用缓存时不会考虑这些内容.在终端会话中使用 touch 可以使问题在下次启动时消失.

After a bit more experimenting, I have an explanation...if not a solution. Adding new objects does not change the modification date of my .sqlite file. It updates the .sqlite-shm and .sqlite-wal but I'll guess those aren't considered when judging whether to use the cache. Using touch from a terminal session makes the problem go away for the next launch.

(Xcode 10.1,macOS 10.13.6,部署目标10.3,iOS 12.1模拟器和10.3.2设备)

(Xcode 10.1, macOS 10.13.6, deployment target 10.3, iOS 12.1 simulator and 10.3.2 device)

另一项修改:

我已经在 https://github.com/PhilKMills/上载了一个压缩的项目目录,以演示该问题.CacheTest

我得到的是:第一次运行,两次读取均获得3条记录;第二次运行是6和3.我认为这完全有可能取决于我的特定软件版本,但是目前我无法升级.其他人的结果将是最有趣的.

What I get is: first run, 3 records for both fetches; second run, 6 and 3. I see it as entirely possible that this depends on my particular software versions but I'm not in a position to upgrade at the moment. Other people's results would be most interesting.

注意:如果未分配FRC代表,则不会出现此问题.

Note: without a FRC delegate being assigned, the problem does not appear.

推荐答案

我怀疑问题与FRC缓存的使用有关,尽管我不确定为什么会这样.因此,一种解决方法是放弃使用FRC缓存.但是,这并不理想.

I suspected that the issue related to the use of the FRC cache, though I was uncertain why that might be. One workaround would therefore be to abandon using the FRC cache. However, that's not ideal.

在OP进行了进一步的试验之后,似乎该问题与与持久性存储关联的 .sqlite 文件上的时间戳有关.通过推论(*),如果FRC检测到时间戳已更改,它将意识到其缓存可能已过期,并对其进行重建-从而检测到新添加的对象.但是,由于CoreData默认使用SQLite的"WAL日记模式"(请参见此处此处),则数据库更新不会写入 .sqlite 文件,而是改为单独的 .sqlite-wal 文件:即使数据库已更新, .sqlite 文件上的时间戳因此也未更改.FRC继续使用其缓存,没有意识到其他对象(或其他更改).

After further experimentation by the OP, it seems the problem relates to the timestamp on the .sqlite file associated with the persistent store. By inference(*), if the FRC detects that the timestamp has changed, it realises that its cache might be out of date, and rebuilds it - thereby detecting the newly added objects. However, because CoreData by default uses SQLite's "WAL journal mode" (see here and here), database updates are written not to the .sqlite file, but instead to a separate .sqlite-wal file: the timestamp on the .sqlite file is consequently not changed, even though the database has been updated. The FRC continues to use its cache, unaware of the additional objects (or indeed other changes).

因此,第二种解决方法是放弃使用SQLite的WAL日记模式.这可以通过使用

A second workaround is therefore to abandon using SQLite's WAL journal mode. This can be achieved by specifying the required journal mode when loading the persistent store, using

NSSQLitePragmasOption:@{@"journal_mode":@"DELETE"}

请参见此处.

(*)实际上,这是在此处记录的:

(*) In fact, this is documented here:

"控制器测试高速缓存以确定其内容是否仍然有效.控制器将当前的实体名称,实体版本哈希,排序描述符和节密钥路径与缓存中存储的内容进行比较,以及缓存的信息文件和持久性存储文件的修改日期."

"the controller tests the cache to determine whether its contents are still valid. The controller compares the current entity name, entity version hash, sort descriptors, and section key-path with those stored in the cache, as well as the modification date of the cached information file and the persistent store file."

这篇关于NSFetchedResultsController找到错误数量的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆