NSManagedObject不反映后台线程NSManagedObjectContextDidSaveNotification后的更改

时间:2011-03-25 20:23:05

标签: objective-c cocoa-bindings nsarraycontroller background-process nsmanagedobjectcontext

我遇到问题,NSManagedObject没有反映在后台线程保存上下文后对持久存储所做的更改。

设置

在一个简单的测试应用程序中,我有一个窗口列出了我的核心数据持久存储中的所有对象,一个用于过滤结果的搜索框和一个用于显示所选项目名称的文本字段,并允许名称为改变了。

绑定如下:

ArrayController --> AppDelegate --> ManagedObjectContext
TableView Col 1 --> ArrayController --> values --> arrangedObjects.widgetName
TableView Col 2 --> ArrayController --> values --> arrangedObjects.uid

SearchField --> ArrayController --> predicate --> filterPredicate

TextField --> ArrayController --> value --> selection.widgetName

我还有一个按钮,用于启动从Web服务器获取数据的后台(NSOperation)。

流程

当用户单击刷新按钮时,NSOperation将启动并异步抓取窗口小部件,解析响应,检查要删除的本地窗口小部件(不在响应中),要添加的新窗口小部件未存储在本地和现有的本地小部件,应该使用从服务器检索的数据进行更新。

处理完成后,将使用以下方式通知主要上下文:

[mainContext performSelectorOnMainThread:
       @selector(mergeChangesFromContextDidSaveNotification:) 
                              withObject:notification 
                           waitUntilDone:YES];

我在主控制器中有一个观察员进行测试,显示变化很好,主控制器得到了通知。

问题

如果我使用文本字段对选定对象进行更改,则在保存后台线程上的数据时,UI中的对象不会更新以反映这些更改(即,它不会覆盖UI从服务器更改。)

例如,给定以下三个小部件和ID:

Test Name 1 | ID 123
Test Name 2 | ID 234
Test Name 3 | ID 345

如果我将用户名Test Name 2更改为Renamed 2我有以下内容:

Test Name 1 | ID 123
Renamed 2   | ID 234
Test Name 3 | ID 345

当我在后台刷新时,我希望列表反映服务器的状态,即返回:

Test Name 1 | ID 123
Test Name 2 | ID 234
Test Name 3 | ID 345

相反,它仍然存在:

Test Name 1 | ID 123
Renamed 2   | ID 234
Test Name 3 | ID 345

我知道持久性存储已更新,因为如果我从XCode中删除应用并重新启动,则会显示所需的信息。如果我正常退出应用程序,更改的值将在应用程序关闭时写入存储,重新打开将显示重命名的值。

我尝试过什么

我知道消息是从后台发送到主上下文的,我知道数据正在持久存储到商店。因此,我认为问题是主要的上下文没有像我期望的那样合并,或者我需要以某种方式强制数组控制器从持久性存储中获取并丢弃它的上下文。

  • 我在收到商店保存通知后尝试processPendingChanges:,但我怀疑我只是将Renamed 2写入商店。
  • 我尝试在数组控制器上执行rearrangeObjects,但由于数组控制器处理主要上下文,我怀疑这样做无效
  • 我尝试在数组控制器上执行fetch:nil来从持久性存储中进行提取,但我再次怀疑主要上下文是否覆盖了Renamed 2的值,因为它还没有保存。
  • 我已按照Apple文档在阵列控制器上尝试了fetchWithRequest:nil merge:NO error:&error,但这似乎并没有改变显示的值

我认为需要发生的是阵列控制器在写入后台存储数据之前将其数据保存到持久存储中,以便在阵列控制器上获取将导致数据到达按照我的期望准确。如果确实如此,我将如何告诉数组控制器执行此操作,或者如果以某种方式保存主要的managedObjectContext,数组控制器是否会通过绑定知道更改?

我可以通过从持久性存储中获取,将数据放入数组并在数组控制器上执行setContent:然后在保存持久存储时重复此操作来破解解决方案,但这感觉很简单错误,更不用说必须跟踪阵列控制器的选定状态的问题(以及可能由于该主要选择而可能发生的任何子阵列选择)。

我离开基地吗?我显然在这里遗漏了一些东西。

任何有智慧或建议的话都会非常感激。

1 个答案:

答案 0 :(得分:3)

沮丧的力量加上决心啊。我不确定这是否是最好的或推荐的方法,但它肯定符合我的需要。

我的目标是在保存后台更新之前保留任何非持久性更改,或者在保存后台更新之前将其丢弃。两者在我的世界中都有相同的用途(服务器数据总是正确的)。

原来我只需要在我的NSOperation中添加一个观察者:

NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self 
       selector:@selector(prepareMerge:) 
           name:NSManagedObjectContextWillSaveNotification object:ctx];

在操作中调用方法:

-(void)prepareMerge:(NSNotification *)notification {

    [[NSNotificationCenter defaultCenter] 
         postNotificationOnMainThreadName:@"SaveNow"
                                   object:nil];
}

通知发送到主线程(由{NSMotificationCenter上发布的cocoanetics.com上的类别提供),该主线程在相关类中被监听:

[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(saveNow:) 
                                             name:@"SaveNow" 
                                           object:nil];

当然是实际做出改变的方法:

-(void)saveNow:(NSNotification *)aNote {

    [[self managedObjectContext] rollback];
}

在提交保存之前,阵列控制器和具有更新值的任何其他UI组件都会回滚。保存完成后,旧的本地值全部替换为新值。

完成工作。