objective-c - NSManagedObject 不反射(reflect)后台线程 NSManagedObjectContextDidSaveNotification 后的更改

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

我遇到了 NSManagedObject 的问题,它没有反射(reflect)在后台线程保存其上下文后对持久存储所做的更改。

设置

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

绑定(bind)如下:

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

I also have a button that starts a background (NSOperation) fetch of data from a web server.

The Process

When the user clicks the refresh button, an NSOperation is kicked off that goes and grabs the widgets asynchronously, parse the response, checks for local widgets to delete that are not in the response, new widgets to add that are not stored locally and existing local widgets that should be updated with the data retrieved form the server.

Once the processing has finished, the main context is notified using:

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

我在主 Controller 中有一个观察器用于测试,它显示更改进行得很好并且主 Controller 收到了通知。

问题

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

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

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

如果我将 Test Name 2 的 UI 中的名称更改为 Renamed 2 我有以下内容:

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

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

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 中终止该应用程序并重新启动,则会显示所需的信息。如果我正常退出应用程序,更改的值会在应用程序关闭时写入商店,重新打开时会显示重命名的值。

我尝试过的

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

  • 我已尝试 processPendingChanges: 收到商店保存通知,但我怀疑我只是将 Renamed 2 写入商店。
  • 我曾尝试在数组 Controller 上执行 rearrangeObjects,但由于数组 Controller 正在处理主要上下文,我怀疑这什么也没做
  • 我尝试在数组 Controller 上执行 fetch:nil 以从持久存储中获取数据,但我再次怀疑主上下文正在覆盖 Renamed 2 的值 因为它还没有保存。
  • 我已经按照 Apple 文档在数组 Controller 上尝试了 fetchWithRequest:nil merge:NO er​​ror:&error 但这似乎并没有改变显示的值

认为需要做的是让阵列 Controller 在我写入后台存储数据之前将其数据保存到持久存储中,这样阵列 Controller 上的提取将导致数据按照我的期望准确。如果确实是这种情况,我将如何告诉数组 Controller 执行此操作,或者如果主 managedObjectContext 以某种方式保存,数组 Controller 是否仅通过绑定(bind)知道更改?

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

我错了吗?我显然在这里遗漏了一些东西。

如有任何智慧之言或建议,我们将不胜感激。

最佳答案

啊,挫折的力量加上决心。我不确定这是否一定是最佳或推荐的方法,但它肯定满足我的需求。

我的目标是让任何非持久化的更改要么在保存后台更新之前持续存在,要么在保存后台更新之前被丢弃。两者在我的世界中服务于相同的目的(服务器数据总是正确的)。

原来我只需要在我的 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];
}

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

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

当然还有实际进行更改的方法:

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

    [[self managedObjectContext] rollback];
}

数组 Controller 和任何其他具有更新值的 UI 组件会在提交保存之前立即回滚。保存完成,旧的本地值全部替换为新值。

工作完成。

关于objective-c - NSManagedObject 不反射(reflect)后台线程 NSManagedObjectContextDidSaveNotification 后的更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5437734/

相关文章:

ios - 如何获取 TableView 的第一个单元格?

objective-c - Cocoa Bindings,我应该只使用 KVO 吗?

objective-c - NSTableView,多个单元格和绑定(bind)

objective-c - NSArrayController 更改不会传播到绑定(bind)的 NSUserDefaults

cocoa - tableCellView 内的 NSButton : how to use the "action invocation" for a View Based NSTableView/NSArrayController

ios - 具有默认样式分组的 UITableView 子类

ios - 通过xcode5的xib设计时导航栏按钮item图片颜色不同

iphone - 由于获取的结果 Controller 更新,释放的 View Controller 导致 EXC_BAD_ACCESS

swift - Swift 中基于 View 的表绑定(bind)

swift - 将 NSPopupbutton 绑定(bind)到类数组