【问题标题】:NSManagedObjectContext: How to update main contextNSManagedObjectContext:如何更新主上下文
【发布时间】:2011-12-15 06:34:47
【问题描述】:

我有一个使用 Core Data 和默认 AppDelegate 的项目。我的代码中有以下线程,其中下载了我的 NSManagedObject WSObject 的图像。你会注意到,我正在为这个后台线程创建一个新的NSManagedObjectContext。我尝试阅读网络上的不同文档和其他论坛主题,但无法理解在我的对象保存在后台上下文中后如何在 AppDelegate 中通知我的主要上下文。

- (void) downloadImageForObjectID:(NSManagedObjectID*)objectID {
    dispatch_queue_t imageDownloaderQueue = dispatch_queue_create("imagedownloader", NULL);
    dispatch_async(imageDownloaderQueue, ^{
        NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
        context.persistentStoreCoordinator = [(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator];
        context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;

        WSObject *item = (WSObject*)[context objectWithID:objectID];
        item.image.data = [item.image download];

        if ([context hasChanges]) {
            NSError *error = nil;
            [context save:&error];
        }
    });
    dispatch_release(imageDownloaderQueue);
}

有人可以告诉我要在这个方法和 AppDelegate 中添加什么来让它工作吗?据我了解,当我在后台线程中保存上下文时会发送NSManagedObjectContextDidSaveNotification。我应该在我的 AppDelegate 中添加什么代码来收听这个通知以及收到通知时应该做什么?

EDIT1: 我已将观察者添加到后台线程。

if ([context hasChanges]) {
    NSError *error = nil;


    NSManagedObjectContext *mainContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeHandler:) name:NSManagedObjectContextDidSaveNotification object:mainContext];

    [context save:&error];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:mainContext];
}

但 AppDelegate 中的 mergeHandler 永远不会被调用。

【问题讨论】:

  • 错误的对象传递给addObserver:selector:name:object:object 应该是 context

标签: objective-c core-data nsmanagedobjectcontext


【解决方案1】:

在使用您为NSManagedObjectContextDidSaveNotification 注册的 AppDelegate 类定义的通知处理程序中,您只需执行以下操作:

- (void)myManagedObjectContextDidSaveNotificationHander:(NSNotification *)notification
{
    // since we are in a background thread, we should merge our changes on the main
    // thread to get updates in `NSFetchedResultsController`, etc.
    [self.managedObjectContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) withObject:notification waitUntilDone:NO];
}

假设self.managedObjectContext 指的是你的主要NSManagedObjectContext,那么就是这样。

最简单的可能是在保存之前注册您的上下文并在之后取消注册:

    if ([context hasChanges]) {
        NSError *error = nil;

       [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myManagedObjectContextDidSaveNotificationHander:) name:NSManagedObjectContextDidSaveNotification object:context];

       [context save:&error];

       [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:context];
    }

【讨论】:

  • 我应该在 AppDelegate 的哪个位置添加 NSManagedObjectContextDidSaveNotification 的观察者?我应该在哪里移除观察者?
  • 添加了更多可能有帮助的信息
  • 我假设您的编辑是针对后台线程的?我的后台线程不知道主上下文,所以我不能这样做。与其让每个后台线程调用主上下文进行更新,我认为如果主上下文监听通知本身会很有用。
  • 你当然可以这样做。但是您需要要观察的上下文,否则在添加观察者时您将获得所有 NSManagedObjectContextDidSaveNotification' notifications if you pass nil` 对象。这就是为什么我建议在之前和之后注册/取消注册。该方法所在的类还可以维护对主上下文的引用并在内部处理同步(当然,仍然为后台工作创建一个新的上下文)。
  • 我做到了。您将错误的NSManagedObjectContext 传递给object:。传递context 引用的那个,因为那是您要监控的那个。
【解决方案2】:

要跟进 gschandler,

在您的 appDelegate 中,您可以这样做:

 [[NSNotificationCenter defaultCenter] addObserver:self 
                 selector:@selector(myManagedObjectContextDidSaveNotificationHander:) 
                     name:NSManagedObjectContextDidSaveNotification 
                   object:nil];

如果您将nil 作为对象传递,您将收到您指定的name 的所有通知,无论是哪个对象发送的。

NSNotificationCenter Class Reference

通知发送者
观察者想要接收其通知的对象;也就是说,只有这个发送者发送的通知才会发送给观察者。
如果你传递 nil,通知中心不会使用通知的发送者来决定是否发送给观察者。


有了这个,你还应该从你的主线程上下文中收到通知,所以你需要做一些过滤以避免与主线程保存循环,获得通知发生更改并再次保存并获得通知等。

【讨论】:

  • 采用霰弹枪方法可能完全没问题。我猜你只需要检查[notification object] 并确保它与主上下文本身不是同一个对象。即使这样,将保存中的更改合并到相同的上下文中也可能没什么大不了的。
  • @gschandler 每次我完成编辑时,您添加的评论与我刚刚添加的内容很接近,如果可能的话,我会给您另一个 +1 :)
  • 如果我将 addObserver 放在主上下文的 AppDelegate 中,那么在 AppDelegate 中的哪个位置是添加 addObserver 和 removeObserver 的合适位置?我应该将它添加到 init 和 dealloc 中吗?
  • @DennisMadsen 基本上,在你需要它之​​前添加它,一旦你不再需要它就删除它。所以 applicationDidFinish... 可能是一个创建它的好地方,你也应该在进入后台和其他生命周期方法时处理。但也取决于您构建应用程序的方式,其他地方可能更有意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多