【问题标题】:NSFetchedResultsController doesn't show updates from a different contextNSFetchedResultsController 不显示来自不同上下文的更新
【发布时间】:2012-05-12 18:49:50
【问题描述】:

我有一个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

提前谢谢你。

编辑

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

建议?

编辑 2

这是我在NSFetchedResultsController getter 中使用的谓词。是我的错,但是我写的时候没有抄。

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 属性。

【问题讨论】:

  • 查看我对这个问题的回答。我认为这可能会有所帮助:stackoverflow.com/questions/3923826/…
  • 通过阅读您的问题,我认为@ArturFriesen 是正确的:NSFetchedResultsController 忽略了发生在它自己或其子上下文之外的更改。您需要在主 moc 中的每个相关对象上调用 refreshObject。另一种方法:stackoverflow.com/a/21378533/313633

标签: ios core-data nsfetchedresultscontroller nsmanagedobjectcontext nsoperationqueue


【解决方案1】:

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

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

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

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

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

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

编辑

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

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

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

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

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

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

【讨论】:

  • 许多框架在内部使用 Core Data。确保您不会错误地合并这些通知。
  • @JodyHagins 首先感谢您的回答。 + 1 感谢您的支持。我在编辑中写的,我认为,这不是问题。也许我改变了一些东西,但我不知道是什么。您能否解释一下 是什么意思...但是您包含的代码示例看起来很难将所有正在加载的对象的 isSync 设置为 YES,但是您的获取请求只希望那些 isSync 设置为 NO 的对象。这些新对象都不会通过该谓词。?提前谢谢你。
  • @AlexsanderAkers 感谢您的评论。
  • 在您的 NSOperation 的 main() 中,您正在加载新对象,并且看起来您正在将每个新对象的 isSync 设置为 YES。您用于 fetchedResultsController 的谓词仅查找具有 isSync == NO 的对象。另外,请解决我提出的其他问题(尤其是缺少排序描述符)。
  • 使用 CoreData 可以这么复杂,当你遇到问题时,你几乎必须提供所有代码,因为很多东西都是紧耦合的。我会在保存之前设置一个断点(或 NSLog),然后转储正在保存的整个 moc(我实际上有帮助代码来执行此操作,这对我来说很常见)。我对 CoreData 的规范调试过程:我有 NSManagedObjectContext 和 NSPersistentStoreCoordinator 的子类,它们只是 NSLog 并调用 super。我还注册每个通知,并 NSLog 它们。我建议你做一些类似的事情来了解真正发生的事情。
【解决方案2】:

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

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

解决后我做了一些笔记:

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

初始化managedObjectContext有两种方法:

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

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

注意:不能同时设置managedObjectContextpersistentStoreCoordinatorparentContext 属性。

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

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

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

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

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

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

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

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

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

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

希望对你有帮助!

编辑:这是一个对我有很大帮助的 stackoverflow 线程:Does a Core Data parent ManagedObjectContext need to share a concurrency type with the child context?

【讨论】:

  • 感谢分享+1,我不明白两个上下文都需要用initWithConcurrencyType:NSMainQueueConcurrencyType 方法初始化。。这是什么意思?
  • 两个上下文都需要用 initWithConcurrencyType:NSMainQueueConcurrencyType 方法初始化——这是错误的。
猜你喜欢
  • 2011-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-15
  • 2012-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多