cocoa - NSFetchedResultsController 缓存再次不匹配

标签 cocoa cocoa-touch exception core-data nsfetchedresultscontroller

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

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:

但我还没有做过这些。要理解这个问题,请查看谓词:

predicate: (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 委托(delegate)方法,尤其是当我将两个对象的状态更改为 0 时
  • 如果我在 performFetch: 之前清空缓存(或者没有缓存),一切都会正常运行。
  • 在定期检查上面提到的外部条件结束时,我在 Controller 上调用此 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)。

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

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

最佳答案

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

不确定它是否相关,但我会对任何包含未转义分号的语句感到紧张。 ";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' 至少用于故障排除。

关于cocoa - NSFetchedResultsController 缓存再次不匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5393931/

相关文章:

macos - 如何将 NSObjectController 与 Core Data 结合使用?

iphone - 如何以编程方式向 View 添加多个标签而不用担心其 Y 位置?

java - 使用 RMI 将 Java Vector 从服务器返回到客户端

临界区中的 C++ 异常处理(pthreads)

objective-c - 我应该将通过 Interface Builder 进行的委托(delegate)引用设为 nil 吗?

objective-c - 使用 Grand Central Dispatch 进行 Internet 请求的良好模式?

iphone - 当 UIImageView 离开 UIViewController 的框架时屏蔽它

ios - dispatch_semaphore_dispose 上的 EXC_BAD_INSTRUCTION(代码=EXC_I386_INVOP,子代码=0x0)

objective-c - 在 Cocoa 应用程序中进行 FTP 上传?

ios - 将 UIButton 中的图像缩放到 AspectFit?