23

如何检测对特定属性的更改NSManagedObject?在我的核心数据数据模型中,我有一个Product代表待售产品的实体。Product实体有几个属性:price, sku, weight,numberInStock等。每当 a 的属性price发生Product变化时,我都需要进行冗长的计算。因此,我想知道任何更改的price属性, [编辑]即使该更改来自合并保存在另一个线程上的上下文。这样做的好方法是什么?我的商店里有成千上万的物品;显然,向每个人发送一条消息是不可行的。 ProductProductaddObserver

我一直在使用NSManagedObjectContextObjectsDidChangeNotification来检测更改,但它只通知我托管对象已更改,而不是该对象的哪个属性已更改。只要 a 有任何更改,我就可以重做计算Product,但是只要不相关的属性发生更改,就会导致无用的重新计算。我正在考虑制作一个Price实体(仅包含一个属性)并使用andprice之间的一对一关系。这样,我可以检测到对象的变化以开始计算。这对我来说似乎过于笨拙。有没有更好的办法?ProductPricePrice

更新:

@railwayparade 指出我可以使用 的changedValues方法NSManagedObject来确定每个更新对象的哪些属性发生了变化。我完全错过了那个方法,如果没有在后台线程上进行更改并合并到主线程的上下文中,它将完全解决我的问题。(见下一段。)

我完全错过了NSManagedObjectContextObjectsDidChangeNotification工作方式的一个微妙之处。据我所知,当保存在另一个线程上的托管对象上下文被合并到主线程上的上下文中时(使用 a mergeChangesFromContextDidSaveNotification:),结果NSManagedObjectContextObjectsDidChangeNotification 包含有关当前在主线程的托管对象上下文中的对象的更改信息。如果更改的对象不在主线程的上下文中,则它不会成为通知的一部分。这是有道理的,但不是我所期待的。因此,我使用一对一关系而不是属性来获取更详细的更改信息的想法实际上需要检查后台线程的NSManagedObjectContextDidSaveNotification,而不是主线程的NSManagedObjectContextObjectsDidChangeNotification。当然,简单地使用以下changedValues方法会更聪明NSManagedObject正如@railwayparade 有用地指出的那样。但是,我仍然存在一个问题,即来自主线程合并的更改通知不一定包含在后台线程上所做的所有更改。

4

5 回答 5

43

关于这个线程的一点,

Core Data 生成的 NSManagedObjectContextObjectsDidChangeNotification 表示托管对象发生了变化,但不表示哪个属性发生了变化。

它确实如此。“changedValues”方法可用于查询哪些属性发生了变化。

就像是,

 if([updatedObjects containsKindOfClass:[Config class]]){
    //if the config.timeInterval changed
    NSManagedObject *obj = [updatedObjects anyObject];
    NSDictionary *dict=[obj changedValues];
    NSLog(@"%@",dict);
    if([dict objectForKey:@"timeInterval"]!=nil){
      [self renderTimers];
    }
  }
于 2011-06-23T02:14:36.713 回答
10

这种情况是您需要自定义 NSManagedObject 子类的地方。您需要子类,因为您正在向托管对象添加行为,对价格变化做出反应。

在这种情况下,您将覆盖price属性的访问器。使用数据模型编辑器中的弹出菜单创建自定义子类。然后选择price属性并选择“将 Obj-C 2.0 实现复制到剪贴板”。它会给你很多东西,但关键部分看起来像这样:

- (void)setPrice:(NSNumber *)value 
{
    [self willChangeValueForKey:@"price"];
    [self setPrimitivePrice:value];
    [self didChangeValueForKey:@"price"];
}

只需添加处理价格变化的代码即可。任何时候特定产品的价格发生变化,代码都会运行。

于 2010-10-12T21:47:51.227 回答
3

你可以看看 KVO(键值观察)。不确定 Core Data API 中是否内置了包装器,但我知道它是 Objective-C 的一部分。

于 2010-10-10T16:55:13.847 回答
1

我想我会在这里记录我的设计决策,以防它们对其他人有用。我的最终解决方案是基于 TechZen 的回答。

首先,我将从一个简短且希望更清晰的问题重述开始:

在我的应用程序中,我想检测对price托管对象 ( Product) 的特定属性 ( ) 的更改。此外,我想知道这些更改是在主线程还是后台线程上进行的。Product最后,即使主线程当前在其托管对象上下文中没有更改的对象,我也想知道这些更改。

NSManagedObjectContextObjectsDidChangeNotificationCore Data 生成的 表示托管对象已更改,但不表示哪个属性已更改。我的 kludgy 解决方案是创建一个Price包含单个price属性的托管对象,并将该price属性替换为与托管对象Product的一对一关系Price。现在,每当对托管对象进行更改时Price,Core Data都会在其集合中NSManagedObjectContextObjectsDidChangeNotification包含该对象。我只需要将这些信息传递给主线程。这一切听起来不错,但有一个障碍。PriceNSUpdatedObjectsKey

我的核心数据存储由两个线程操作。这是以“通常”的方式完成的——每个线程都有一个托管对象上下文和一个共享的持久存储协调器。后台线程进行更改后,它会保存其上下文。主线程通过 检测上下文保存NSManagedObjectContextDidSaveNotification并使用 合并上下文更改mergeChangesFromContextDidSaveNotification:。(实际上,由于通知是在它们发布的同一线程中NSManagedObjectContextDidSaveNotification接收的,因此在后台线程上接收并通过performSelectorOnMainThread:合并传递给主线程。)作为合并的结果,Core Data 生成一个NSManagedObjectContextObjectsDidChangeNotification指示更改的对象. 然而,据我所知,NSManagedObjectContextObjectsDidChangeNotification仅包括当前在接收上下文中表示的那些对象。从更新 UI 的角度来看,这是有道理的。如果未显示托管对象,则它可能不在上下文中,因此无需将其包含在通知中。

就我而言,我的主线程需要知道对托管对象所做的更改,无论它们当前是否位于主线程的上下文中。如果有任何价格变化,主线程需要排队一个操作来处​​理该价格变化。因此,主线程需要了解所有价格变化,即使这些更改是在后台线程上对当前未在主线程上访问的产品进行的。显然,由于NSManagedObjectContextObjectsDidChangeNotification只包含有关当前在主线程上下文中的对象的信息,它不符合我的需要。

我想到的第二个选项是NSManagedObjectContextDidSaveNotification在保存其上下文时使用由后台线程生成的。此通知包含有关托管对象的所有更改的信息。我已经检测到这个通知并将它传递给主线程进行合并,那么为什么不看看里面并查看所有已更改的托管对象呢?您会记得托管对象并不意味着跨线程共享。因此,如果我开始检查NSManagedObjectContextDidSaveNotification主线程上的内容,我会崩溃。嗯……那怎么mergeChangesFromContextDidSaveNotification:做呢?显然,mergeChangesFromContextDidSaveNotification:它是专门为解决“不要跨线程共享托管对象”限制而设计的。

NSManagedObjectContextDidSaveNotification我想到的第三个选项是在后台线程上注册,并在后台线程上将其内容转换为特殊PriceChangeNotification的包含对象 ID 而不是托管对象。在主线程上,我可以将对象 ID 转换回托管对象。这种方法仍然需要一对一的Price关系,以便价格的变化反映为Price托管对象的变化。

我的第四个选项基于 TechZen 的建议,即覆盖Product托管对象中的价格设置器。我没有使用一对一的关系来强制 Core Data 生成我需要的通知,而是回到使用price属性。在我的setPrice方法中,我发布了一个自定义PriceChangeNotification. 此通知在后台线程上接收,用于构造一组Product具有价格变化的对象。在后台线程保存其上下文后,它会发布一个自定义PricesDidChangeNotification,其中包括所有Product价格已更改的对象的对象 ID。该通知可以安全地传输到主线程并进行检查,因为它使用对象 ID 而不是托管对象本身。在主线程上,我可以获取Product这些对象 ID 引用的对象并排队操作以在新的后台线程上执行冗长的“价格变化”计算。

于 2010-10-16T20:33:27.657 回答
0

您使用的是NSArrayController控制器还是其他控制器?大概您需要某种方式让用户与模型进行交互。正是这个交互点为这种类型的更新调用提供了一个很好的钩子。也许适当的策略是观察数组控制器的相关属性arrangedObjects

于 2010-10-10T17:43:11.500 回答