【问题标题】:NSManagedObject Not Reflecting Changes After Background Thread NSManagedObjectContextDidSaveNotificationNSManagedObject 在后台线程 NSManagedObjectContextDidSaveNotification 后不反映更改
【发布时间】:2011-03-25 20:23:04
【问题描述】:

在后台线程保存其上下文后,NSManagedObject 无法反映对持久存储所做的更改。

设置

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

绑定如下:

ArrayController --> AppDelegate --> ManagedObjectContext TableView Col 1 --> ArrayController --> 值 --> 排列对象.widgetName TableView Col 2 --> ArrayController --> 值 --> mappedObjects.uid SearchField --> ArrayController --> 谓词 --> filterPredicate TextField --> ArrayController --> value --> selection.widgetName

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

过程

当用户单击刷新按钮时,会启动一个 NSOperation,它会异步抓取小部件、解析响应、检查本地小部件以删除响应中的内容、添加未存储在本地的新小部件以及应该使用从服务器检索到的数据进行更新的现有本地小部件。

处理完成后,使用以下命令通知主上下文:

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

我在主控制器中有一个观察者进行测试,显示更改进行得很好,并且主控制器得到了通知。

问题

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

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

测试名称 1 |编号 123 测试名称 2 |编号 234 测试名称 3 |编号 345

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

测试名称 1 |编号 123 重命名 2 |编号 234 测试名称 3 |编号 345

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

测试名称 1 |编号 123 测试名称 2 |编号 234 测试名称 3 |编号 345

它仍然存在:

测试名称 1 |编号 123 重命名 2 |编号 234 测试名称 3 |编号 345

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

我的尝试

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

  • 我在商店保存通知时尝试了processPendingChanges:,但我怀疑我只是将Renamed 2 写到商店。
  • 我曾尝试在阵列控制器上执行 rearrangeObjects,但由于阵列控制器正在处理主要上下文,我怀疑这无济于事
  • 我尝试在数组控制器上执行 fetch:nil 以从持久存储中获取数据,但我再次怀疑主上下文正在覆盖 Renamed 2 的值,因为它尚未保存。李>
  • 我已经按照 Apple 文档在阵列控制器上尝试了 fetchWithRequest:nil merge:NO error:&error,但这似乎并没有改变显示的值

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

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

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

非常感谢任何智慧或建议的话语。

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

    工作完成。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多