NSFetchedResultsController缓存再次不匹配

时间:2011-03-22 15:50:40

标签: cocoa cocoa-touch exception core-data nsfetchedresultscontroller

这是另一种情况,在performFetch上调用NSFetchedResultController时会出现例外情况:

  

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

除非我没有做过这些。要了解问题,请查看谓词:

  

谓词:(collection.identifier ==&#34 ;; 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是全局const NSStringupToDate是枚举常量(0)。

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

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

1 个答案:

答案 0 :(得分:0)

我会查看你的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'进行问题排查。