NSFetchedResultsController不显示来自不同上下文的更新

时间:2012-05-03 09:07:55

标签: ios core-data nsfetchedresultscontroller nsmanagedobjectcontext nsoperationqueue

我有一个NSFetchedResultsController和一些操作通过NSOperationQueue在不同的线程上更新托管对象。

FRC(及其谓词)如下所示:

- (NSFetchedResultsController*)fetchedResultsController
{
    if(fetchedResultsController) return fetchedResultsController;

    NSManagedObjectContext* mainContext = [self managedObjectContext];

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    [fetchRequest setEntity:[NSEntityDescription entityForName:@"Check" inManagedObjectContext:mainContext]];
    [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"isSync == %@", [NSNumber numberWithBool:NO]]];
    [fetchRequest setFetchBatchSize:10];

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

    [fetchRequest release], fetchRequest = nil;

    return fetchedResultsController;
}

主线程和线程操作具有自己的托管对象上下文。他们只共享同一个协调员。

在线程操作中,我将isSync属性从NO更改为YES。要知道要更新的Check实体是什么,主要上下文将传递给线程NSManagedObjectID。 线程操作检索托管对象,如下所示:

-(void)main
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSManagedObjectContext *exportContext = [[NSManagedObjectContext alloc] init];
    [exportContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];

    //...

    Check* check = (Check*)[exportContext existingObjectWithID:objID error:&error];
    check.isSync = [NSNumber numberWithBool:YES];

    //...

    [exportContext save:&error];

    [pool release], pool = nil;
}

当线程操作调用save时,调用mergeChangesFromContextDidSaveNotification通知并且主上下文合并更改。

- (void)contextChanged:(NSNotification*)notification
{
    if ([notification object] == [self managedObjectContext]) return;

    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES];
        return;
    }

    [[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}

记录notification潜在客户的说明,以验证是否正确执行了更改。

我的问题

NSFetchedResultsControllerDelegate的委托方法未被调用。

这很奇怪,因为处理相同的上下文(主要的上下文)允许监听更改并调用委托方法,例如删除UITableView中的行对象。

我在SO上发现了一些有同样问题的主题。我已经尝试了所有的解决方法,但我找不到有价值的解决方案:

  1. NSFetchedResultsController not showing updates from other contexts

  2. NSFetchedResultsController not firing delegate method after merging update from background thread

  3. NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContext

  4. 提前谢谢。

    修改

    上面的代码在以前的模型中工作。然后我创建了一个新模型复制(和粘贴)前一个实体,现在它不再起作用了。

    建议?

    修改2

    这是我在NSFetchedResultsController getter中使用的谓词。这是我的错,但是当我写这篇文章时,我没有复制它。

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"insertionDate" ascending:NO];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    
    // previous code here
    [fetchRequest setSortDescriptors:sortDescriptors];
    

    现在,关于 Jody 最后评论

      

    在你的NSOperation的main()中,你正在加载新的对象,并在   看起来你正在为每个新对象设置isSync为YES。   您用于fetchedResultsController的谓词只是查找   对于具有isSync == NO。

    的对象

    我希望当属性isSync设置为YES时,NSFetchedResultsController会观察到更改并删除与谓词不匹配的行。我错了吗?

    请记住,当将更改从后台合并到主线程时,我能够看到很少有对象更新了它们的isSync属性。

2 个答案:

答案 0 :(得分:11)

你有基本的想法,所以你的代码中可能存在一个错误......

仔细检查您是否正确注册以接收来自后台MOC的通知。

注册以接收来自所有对象的所有通知。在该方法中,记录事件及其所有数据。当对象是MOC时,转储其所有属性(尤其是已注册,插入,更新和删除的对象的列表)。

在保存调用之前和之后放置一条日志语句,并在通知处理程序中合并通知。

此外,您省略了很多代码,因此很难知道您实际在做什么,但是您包含的代码示例看起来很难将所有正在加载的对象的isSync设置为YES,但是您的获取请求只需要那些将isSync设置为NO。这些新对象都不会传递该谓词。

最后,仔细检查您的模型定义并确保使用正确的数字类型。这可能是问题的重要原因。

修改

哦是的,我忘了...你的获取请求没有排序描述符。创建FRC时,您的获取请求必须至少包含一个排序描述符...如果您有多个部分,则使用第一个排序描述符将对象分组为多个部分。

跟进Alexsander的评论......我在帖子的开头提到了它,但你肯定不想收听MOC的通知,除非它是众所周知的(当然,除非,您只是为了调试而登录)。你应该知道你正在使用的MOC。

此外,我建议使用父/子MOC进行此类处理,但如果处理得当,您正在做的事情应该有效。

父(私有并发类型) 主要(主要并发类型)

然后,使用您的背景MOC,让他们将主moc设置为其父级。当他们保存时,他们的对象被直接注入主MOC。然后,主MOC可以在以后发出保存以将它们放到磁盘上。

或者,您可以将背景MOC作为父母的父母,然后“主”MOC可以重新发出获取以从父母那里获取数据。

答案 1 :(得分:2)

我遇到了同样的问题,我用父/子上下文解决了这个问题。 这是我遇到的问题。

我正在后台线程中更新我的Core Data对象图,后台线程有自己的managedObjectContext(这是必需的),而我的fetchedResultsController无法获取对数据库所做的更改。

解决之后,我做了一些笔记:

ManagedObjectContext不是线程安全的,这意味着managedObjectContext无法与其他线程共享。如果一个线程需要使用managedObjectContext,那么它将初始化它自己的managedObjectContext。

要初始化managedObjectContext,有两种方法:

  • alloc/init然后设置其persistentStoreCoordinator属性

  • alloc/init然后设置parentContext属性而不是persistentStoreCoordinator属性

注意:我们无法同时设置persistentStoreCoordinator的{​​{1}}和parentContext属性。

当一个上下文在“链接到控制器和需要仅在主线程上使用的UI对象(核心)的后台线程上运行时,需要使用父/子上下文数据文件)。

以下是父/子上下文所需的要求:

  • 父上下文存在于主线程

  • 子上下文存在于后台线程

  • 这两种情境都需要使用managedObjectContext方法进行初始化。

  • 在子上下文中完成一批更改后,两个上下文都需要执行保存操作。这些保存操作需要嵌套在performBlock方法中,即:

    initWithConcurrencyType:NSMainQueueConcurrencyType

编辑:上面的代码实际上是一个坏主意有两个原因:

1)它可以在不保存父上下文的情况下工作。

2)如果父上下文在其上运行,则主线程被阻塞。

我希望它有所帮助!

编辑:这是一个stackoverflow线程,它帮助了我:Does a Core Data parent ManagedObjectContext need to share a concurrency type with the child context?