23

我有一个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属性从更改NOYES。要知道Check要更新的实体是什么,主上下文传递给线程一个 a 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;
}

当线程操作调用通知时savemergeChangesFromContextDidSaveNotification主上下文会合并更改。

- (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 不显示来自其他上下文的更新

  2. 从后台线程合并更新后,NSFetchedResultsController 未触发委托方法

  3. 带有谓词的 NSFetchedResultsController 忽略从不同 NSManagedObjectContext 合并的更改

先感谢您。

编辑

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

建议?

编辑 2

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

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"insertionDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];

// previous code here
[fetchRequest setSortDescriptors:sortDescriptors];

现在,关于乔迪的最后评论

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

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

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

4

2 回答 2

11

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

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

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

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

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

最后,仔细检查您的模型定义并确保您使用了正确的数字类型。这可能是问题的一大来源。

编辑

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

跟进 Alexsander 的评论......我在帖子开头提到了它,但你当然不想听 MOC 的通知,除非它是众所周知的你的一个(除非,当然,你是仅出于调试目的而记录)。您应该了解您正在使用的 MOC。

此外,我建议使用父/子 MOC 进行此类处理,但如果操作正确,您正在做的事情应该可以工作。

Parent(私有并发类型) Main(主要并发类型)

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

或者,您可以将背景 MOC 设置为“父级”,然后“主” MOC 可以重新发出 fetch 以从父级获取数据。

于 2012-05-07T18:25:22.140 回答
2

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

我在有自己的后台线程managedObjectContext(这是强制性的)中更新我的核心数据对象图,我fetchedResultsController无法获取对数据库所做的更改。

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

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

初始化一个managedObjectContext有两种方法:

  • alloc/init然后设置它的persistentStoreCoordinator属性

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

注意:不能同时设置 a 的persistentStoreCoordinatorparentContext属性managedObjectContext

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

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

  • 父上下文存在于主线程中

  • 子上下文位于后台线程中

  • 两个上下文都需要使用initWithConcurrencyType:NSMainQueueConcurrencyType方法进行初始化。

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

    childContext performBlock:^{
        [childContext save:nil];
        [self.parentContext performBlock:^{
            [self.parentContext save:nil];                
        }];
    }];
    

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

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

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

我希望它有帮助!

编辑:这是一个对我有很大帮助的stackoverflow线程:Core Data parent ManagedObjectContext是否需要与子上下文共享并发类型?

于 2012-07-23T18:51:03.273 回答