【问题标题】:Core Data: Emitting KVO notifications for transient, derived properties核心数据:为瞬态、派生属性发出 KVO 通知
【发布时间】:2013-05-20 05:47:30
【问题描述】:

我有一个 Parent 实体,该实体具有一个自定义类,该类具有一个名为 DerivedProperty 的瞬态和派生(只读)属性。

DerivedProperty 的值取决于Parent.IndependentProperty1 的值,因此每当IndependentProperty1 更改时,DerivedProperty 的值也会更改。但是,ParentChild(称为children)具有一对多关系,并且DerivedProperty 还依赖于IndependentProperty2 在所有ParentChild 对象中的值。

因此,每当ParentIndependentProperty1IndependentProperty2 的任何Child 对象发生变化时,我想通知任何观察者DerivedProperty 也发生了变化。

到目前为止,我已经获得了以下代码。唯一的问题是没有为DerivedProperty 发出KVO 通知,因为如果我尝试在objectContextDidChange: 中执行此操作,那么代码将最终进入循环。

- (void) awakeFromInsert
{
    [super awakeFromInsert];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(objectContextDidChange:) name:NSManagedObjectContextObjectsDidChangeNotification object:self.managedObjectContext];
}


- (void) awakeFromFetch
{
    [super awakeFromFetch];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(objectContextDidChange:) name:NSManagedObjectContextObjectsDidChangeNotification object:self.managedObjectContext];
}

- (void) objectContextDidChange: (NSNotification *) notification
{
    NSSet *updatedObjects = [[notification userInfo] objectForKey:NSUpdatedObjectsKey];

    if ([updatedObjects containsObject:self] || [updatedObjects intersectsSet:self.children])
    {
        //clear caches
        _derivedProperty  = nil;
    }
}

- (void) didTurnIntoFault
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (NSString *) DerivedProperty
{
    if (_derivedProperty == nil)
    {
        _derivedProperty  = [self someExpensiveCalculation];
    }
    return _derivedProperty  ;
}

我确信我需要重新考虑我在这里的方法。我尝试使用 KVO 来观察一对多关系的IndependentProperty1IndependentProperty2,但我似乎无法让它正常工作。如果派生属性不依赖于一对多关系,那么我确信我可以只使用keyPathsForValuesAffectingValueForKey:,但当然这不适用于一对多关系。

如何使用依赖于一对多关系的 Core Data 瞬态派生属性获得 KVO 通知?

【问题讨论】:

  • 我目前无法对此进行测试,但是反过来如何工作:Children 实体的IndependentProperty2 的setter 函数调用其父级上的一个方法,该方法计算并设置DerivedProperty ?
  • 这可行,但与我的域模型不匹配。 Child 对象不应该对他们的Parent 有任何了解,从不负责更新它。不过我可能最终不得不走这条路。

标签: ios cocoa-touch core-data key-value-observing


【解决方案1】:

首先,您在-objectContextDidChange: 方法中访问DerivedProperty 的ivar,这会使KVO 通知短路。您应该真正在内部将属性重新实现为读/写并使用生成器访问器。

其次,NSManagedObject 的子类默认关闭自动 KVO。这是 Core Data 架构的一部分。因此,如果您不打算使用访问器,您可能希望自己触发通知:

- (void) objectContextDidChange: (NSNotification *) notification
{
    NSSet *updatedObjects = [[notification userInfo] objectForKey:NSUpdatedObjectsKey];

    if ([updatedObjects containsObject:self] || [updatedObjects intersectsSet:self.children]) {
        //clear caches
        [self willChangeValueForKey:@"derivedProperty"];
        _derivedProperty  = nil;
        [self didChangeValueForKey:@"derivedProperty"];
    }
}

注意

在这种情况下,OP 使用瞬态属性 AND 直接访问该属性的 iVar。这有效地创建了两个 iVar,一个由 Core Data 维护,另一个由 OP 的代码维护。这导致了碰撞。

如果您使用的是瞬态属性,建议您按照建议将计算移至另一种方法,并将访问器留给 Core Data。

【讨论】:

  • 关于你的第一点,我正在这样做。外部接口公开readonly,而内部接口将其重新定义为readwrite。我可能放弃了它,因为正如您所说,NSManagedObject 禁用了自动 KVO。我现在无法测试您的建议,但我敢肯定,当我尝试触发这样的通知时,我只是陷入了循环 - derivedProperty 已更改,因此 objectContextDidChange: 会再次被调用,依此类推。
  • 如果你得到一个循环,那么你在两个你不想做的地方都在做 -willChange。理想情况下,您应该有一个 -recalculate 方法来更改值,以便您可以根据需要从 -objectContextDidChange:-derivedProperty 调用 -recalculate。那么-recalculate 将是唯一更改值并调用-willChangeValueForKey:-didChangeValueForKey: 的地方。
  • 我已按照您的建议尝试将willChange...didChange... 放入objectContextDidChange. 中,当我尝试在MOC 上调用保存时,它永远不会返回并且似乎处于无限通知循环中。课堂上没有其他人发出这些willChange...didChange... 调用。有什么想法吗?
  • 从 MOM 中删除 derivedProperty 似乎已停止循环。以前它被定义为模型中的瞬态属性。
  • 这是有道理的。如果您在模型中有瞬态,那么您直接操作的 ivar 与 Core Data 期望您操作的 ivar 不同(或不应该相同)。仅使用访问器并将计算移至另一种方法的另一个原因。
猜你喜欢
  • 2011-06-17
  • 2014-11-26
  • 2011-12-18
  • 1970-01-01
  • 2014-11-26
  • 2012-03-10
  • 1970-01-01
  • 2019-09-08
  • 2013-06-26
相关资源
最近更新 更多