NSFetchedResultsController 缓存再次不匹配

发布于 2024-10-25 11:21:08 字数 4586 浏览 0 评论 0原文

这是另一种情况,在 NSFetchedResultController 上调用 performFetch 会中断并出现异常:

致命错误:节信息的持久缓存与当前配置不匹配。您非法改变了 NSFetchedResultsController 的获取请求、其谓词或其排序描述符,而没有禁用缓存或使用 +deleteCacheWithName:

除非我没有这样做。要理解这个问题,请查看谓词:

谓词:(collection.identifier == ";root" AND doc.status == 0);

现在看看不匹配的缓存内容:

cached objects were: (
  "MyDocument 'PL00002.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00003.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00003.pdf' in set '/' (#2147483647)",
  "MyDocument 'PL00002.pdf' in set '/' (#2147483647)"
)
fetched objects are: (
  "MyDocument 'PL00002.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00003.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00004.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00003.pdf' in set '/' (#2147483647)",
  "MyDocument 'PL00002.pdf' in set '/' (#2147483647)",
  "MyDocument 'PL00004.pdf' in set '/' (#2147483647)"
)

确实,有两个新的匹配对象。原因是两个新对象(PL00004.jpegPL00004.pdf)之前的 status 属性值为 <> 0,我在代码中将它们的 status 更改回 0,因为它们开始满足某些外部条件(不是由用户操作触发)。

为了完整起见,这里是触发异常的代码:

- (void) reloadData
{
  NSError *error = nil;
  if (![[self fetchedResultsController] performFetch:&error]) {
    abort(); // I have actually better error handling
  }       
  [_gridView reloadData];
}

另请注意:

  • 没有其他 NSFetchedResultController 使用同名的缓存
  • _gridView 不是 UITableView >,尽管它也显示实体的集合。这个细节不应该是相关的。
  • 没有任何一个 NSFetchedResultController 委托方法被调用,尤其是当我将两个对象的状态更改为 0 时,
  • 如果我在 performFetch: 之前清空缓存,一切都会正常运行(或者没有缓存)。
  • 在定期检查上面提到的外部条件结束时,我在控制器上调用此 reloadData 方法。
  • NSFetchedResultController 是使用 nil sectionNameKeyPath 创建的。
  • 正如所解释的,我从不改变 fetchedResultsController :不改变它的谓词,不改变它的获取请求,不改变它的排序顺序,什么都没有。

我认为我不应该仅仅因为新对象现在满足谓词而得到这个异常。删除缓存可以避免异常,但只要我不明白为什么应该这样做,我就不认为这是一个修复。

知道我想念这里什么吗?

编辑:这里是请求的信息:

设置 NSFetchedResultController 的代码。当用户选择一个集合时,将调用:

- (void) displayCollection:(Collection *)aSet
{
    self.currentCollection = aSet;      
    NSFetchRequest *fetchRequest = [[self fetchedResultsController] fetchRequest];
    NSString *collectionIdentifier = @";root"; // no selection is like "select root collection";
    if (aSet) {
        collectionIdentifier = aSet.identifier;
    }
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"collection.identifier == %@ and doc.status == %d", collectionIdentifier, upToDate];
    [fetchRequest setPredicate:predicate];

    [NSFetchedResultsController deleteCacheWithName:cacheName];

    NSError *error = nil;
    if (![[self fetchedResultsController] performFetch:&error]) {
        abort(); // whatever
    }       
    [_gridView reloadData];
}

fetchedResultsController 已初始化:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"DocInCollection" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"collection.identifier == %@ and doc.status == %d", @";root", upToDate];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"position" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:cacheName];

doc.status 更新时调用的代码:

- (void) setUpToDate
{
  self.status = [NSNumber numberWithInt:upToDate];
  NSError *error = nil;
  [[self managedObjectContext] save:&error];
  if (error) { } // whatever
  [[self managedObjectContext] processPendingChanges];
}

cacheName是一个全局常量 NSStringupToDate 是一个枚举常量 (0)。

最后,由于应用程序使用了其他实体,该模型有点复杂。这是一个简化版本:

模型 http://emberapp.com/jdmuys /images/untitled-1/sizes/m.png

This is another case where calling performFetch on an NSFetchedResultController breaks with an exception saying:

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:

Except I haven't done any of this. To understand the problem, look at the predicate:

predicate: (collection.identifier == ";root" AND doc.status == 0);

Now look at the mismatched cache contents:

cached objects were: (
  "MyDocument 'PL00002.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00003.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00003.pdf' in set '/' (#2147483647)",
  "MyDocument 'PL00002.pdf' in set '/' (#2147483647)"
)
fetched objects are: (
  "MyDocument 'PL00002.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00003.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00004.jpeg' in set '/' (#2147483647)",
  "MyDocument 'PL00003.pdf' in set '/' (#2147483647)",
  "MyDocument 'PL00002.pdf' in set '/' (#2147483647)",
  "MyDocument 'PL00004.pdf' in set '/' (#2147483647)"
)

Indeed, there are two new matching objects. The reasons is that the two new objects (PL00004.jpeg and PL00004.pdf) had previously their status property to a value <>0 and I changed their status back to 0 in code because they started to satisfy some external condition (not triggered by a user action).

For completeness, here is the code that triggers the exception:

- (void) reloadData
{
  NSError *error = nil;
  if (![[self fetchedResultsController] performFetch:&error]) {
    abort(); // I have actually better error handling
  }       
  [_gridView reloadData];
}

also note:

  • no other NSFetchedResultController uses a cache with the same name
  • _gridView is not a UITableView, though it also displays a collection of entities. This detail should not be relevant.
  • none of the NSFetchedResultController delegate methods are ever called, esp not when I change the status of my two objects to 0
  • everything runs OK if I empty the cache before performFetch: (or with no caching).
  • I call this reloadData method on my controller at the end of my periodical check for that external condition I mentioned above.
  • The NSFetchedResultController is created with an nil sectionNameKeyPath.
  • As explained I never change fetchedResultsController: not its predicate, not its fetch request, not its sort order, nothing.

I don't think I should get this exception simply because new objects now satisfy the predicate. Deleting the cache avoids the exception, but as long as I don't understand why I should do that, I don't consider that as a fix.

Any idea what I miss here?

Edit: here is the requested information:

The code to set up the NSFetchedResultController. When the user chooses a collection, this is called:

- (void) displayCollection:(Collection *)aSet
{
    self.currentCollection = aSet;      
    NSFetchRequest *fetchRequest = [[self fetchedResultsController] fetchRequest];
    NSString *collectionIdentifier = @";root"; // no selection is like "select root collection";
    if (aSet) {
        collectionIdentifier = aSet.identifier;
    }
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"collection.identifier == %@ and doc.status == %d", collectionIdentifier, upToDate];
    [fetchRequest setPredicate:predicate];

    [NSFetchedResultsController deleteCacheWithName:cacheName];

    NSError *error = nil;
    if (![[self fetchedResultsController] performFetch:&error]) {
        abort(); // whatever
    }       
    [_gridView reloadData];
}

fetchedResultsController was initialized thus:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"DocInCollection" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"collection.identifier == %@ and doc.status == %d", @";root", upToDate];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"position" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:cacheName];

The code that is called when doc.status is updated:

- (void) setUpToDate
{
  self.status = [NSNumber numberWithInt:upToDate];
  NSError *error = nil;
  [[self managedObjectContext] save:&error];
  if (error) { } // whatever
  [[self managedObjectContext] processPendingChanges];
}

cacheName is a global const NSString, upToDate is an enum constant (0).

Finally the model is a bit complex because of other entities that are used by the application. Here is a simplified version:

model http://emberapp.com/jdmuys/images/untitled-1/sizes/m.png

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

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

发布评论

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

评论(1

顾忌 2024-11-01 11:21:08

我会查看您的 sectionNameKeyPath 及其与正在突变的属性的关系(如果有)。从错误消息看来,您的部分正在以控制器不喜欢的方式发生变化。

不确定它是否相关,但我会对任何其中包含未转义分号的语句感到紧张。 ";root" 的谓词让我很痒。当然,它的任何问题都应该在编译时出现,但仍然......

更新:

出于病态的好奇心,我使用上面的方法将测试数据模型和一些代码组合在一起。以下内容甚至不会编译:

NSManagedObject *cMO=[NSEntityDescription insertNewObjectForEntityForName:@"Collection" inManagedObjectContext:self.moc];
NSManagedObject *dMO=[NSEntityDescription insertNewObjectForEntityForName:@"Doc" inManagedObjectContext:self.moc];
[cMO setValue:@";root" forKey:@"identifier"];
[dMO setValue:[NSNumber numberWithInt:0] forKey:@"status"];
[cMO setValue:dMO forKey:@"doc"];
NSFetchRequest *fetch=[[NSFetchRequest alloc] init];
[fetch setEntity:[NSEntityDescription entityForName:@"Collection" inManagedObjectContext:self.moc]];
NSPredicate *p=[NSPredicate predicateWithFormat:@"identifier==";root" AND doc.status==0"];
[fetch setPredicate:p];
NSArray *a=[self performFetch:fetch];
NSLog(@"%@",a);

将谓词更改为:

NSPredicate *p=[NSPredicate predicateWithFormat:@"identifier == ';root' AND doc.status == 0"];

...编译并正常工作。我建议您将 ";root" 更改为 ';root' 至少用于故障排除。

I would look at your sectionNameKeyPath and it's relationship, if any, to the attribute being mutated. It would seem from the error message that your sections are changing in a way the controller does not like.

Not sure if it is related at all but I would be nervous of any statement that has an unescaped semicolon in it. That predicate bit of ";root" gives me an itch. Of course, any problems with it should show up at compile time but still...

Update:

Out of morbid curiosity I banged together a test data model and some code using the above. The following will not even compile:

NSManagedObject *cMO=[NSEntityDescription insertNewObjectForEntityForName:@"Collection" inManagedObjectContext:self.moc];
NSManagedObject *dMO=[NSEntityDescription insertNewObjectForEntityForName:@"Doc" inManagedObjectContext:self.moc];
[cMO setValue:@";root" forKey:@"identifier"];
[dMO setValue:[NSNumber numberWithInt:0] forKey:@"status"];
[cMO setValue:dMO forKey:@"doc"];
NSFetchRequest *fetch=[[NSFetchRequest alloc] init];
[fetch setEntity:[NSEntityDescription entityForName:@"Collection" inManagedObjectContext:self.moc]];
NSPredicate *p=[NSPredicate predicateWithFormat:@"identifier==";root" AND doc.status==0"];
[fetch setPredicate:p];
NSArray *a=[self performFetch:fetch];
NSLog(@"%@",a);

Changing the predicate to:

NSPredicate *p=[NSPredicate predicateWithFormat:@"identifier == ';root' AND doc.status == 0"];

... compiles and works properly. I suggest you change the ";root" to ';root' at least for troubleshooting.

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