【问题标题】:Setting up KVO for calculated values, based on calculated values根据计算值为计算值设置 KVO
【发布时间】:2009-03-04 21:19:29
【问题描述】:

所以我有两个对象,Invoice 和 InvoiceLineItem。 InvoiceLineItem 有一个名为cost 的属性,它是基于其他属性动态创建的。为了帮助我使用 KVO/绑定:

+ (NSSet *)keyPathsForValuesAffectingCost {
    return [NSSet setWithObjects:@"lineItemType", @"serviceCost", @"hourlyRate", @"timeInSeconds", @"productCost", @"quantityOfProduct", @"mileageCost", @"milesTraveled", nil];
}

这很好用。当我编辑像 serivceCost 这样的属性时,表视图中的主要成本更新正常。

在 Invoice 对象中,我有一个 InvoiceLineItems 的 NSMutableArray。 Invoice 有一个类似的属性,称为totalCost。它是通过迭代行项目来计算的,只要行项目没有被标记为已删除(我这样做是出于同步原因),它就会将成本相加并创建总成本。

现在我的问题/问题。如何设置 Invoice 的 totalCost 以便在其中一项行项目的成本发生变化时与 KVO/绑定一起使用?

我试过设置:

+ (NSSet *)keyPathsForValuesAffectingTotalCost {
    return [NSSet setWithObjects:@"lineItems.cost", nil];
}

但它不起作用。我最终在控制台中出现错误:[<NSCFArray 0x1499ff40> addObserver:forKeyPath:options:context:] is not supported. Key path: cost

【问题讨论】:

    标签: cocoa cocoa-bindings key-value-observing


    【解决方案1】:

    我认为自动 KVO 传播不支持多关系。文档并没有明确说明一种或另一种方式,但根据我对 KVO 的一般了解,观察一对多关系的子键往往并非易事。

    我解决这个问题的方法是手动观察每个 InvoiceLineItem 对象的 cost 属性,方法是在 Invoice 类上执行 addObserver/removeObserver 调用的 Invoice 类上的 lineItems 属性的一对多 KVC 访问器分别插入/删除方法,然后使用 willChangeValueForKey:/didChangeValueForKey: 手动触发 totalCost 更改。所以像这样(粗略的代码、免责声明等):

    - (void)insertObject:(InvoiceLineItem*)newItem inLineItemsAtIndex:(unsigned)index
    {
        [newItem addObserver:newItem forKeyPath:@"cost" options:0 context:kLineItemContext];
        [lineItems insertObject:newItem atIndex:index];
    }
    
    - (void)removeObjectFromLineItemsAtIndex:(unsigned)index
    {
        [[lineItems objectAtIndex:index] removeObserver:self forKeyPath:@"cost"];
        [lineItems removeObjectAtIndex:index];
    }
    
    - (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
    {
        if (context == kLineItemContext)
        {
            [self willChangeValueForKey:@"totalCost"];
            [self didChangeValueForKey:@"totalCost"];
        }
    }
    

    【讨论】:

    • 我自己非常接近这种实现,但很高兴听到它来自其他人。不过问题是,will/didChange 的堆叠......为什么不直接调用 didChange?
    • 如果底层对象正确实现它,我认为它应该可以工作。例如,Core Data FAQ 显示如下内容:developer.apple.com/documentation/Cocoa/Conceptual/CoreData/…
    • 通过一些早期测试,此方法有效。我也已经准备好了大部分观察内容(用于撤消支持)。再次感谢。
    • 嗯……没关系。该常见问题解答指出“keyPathsForValuesAffectingValueForKey:不允许包含一对多关系的键路径”
    • 它的主要目的是使旧值可以包含在发送给观察者的更改字典中。这在这种情况下并不真正适用,但我认为如果 didChange 调用未事先与 willChange 平衡,KVO 会抱怨。
    【解决方案2】:

    您可以尝试更短的解决方案。

    添加到头文件:

    @property (retain, readonly) NSDecimalNumber *accountBalance;
    

    添加到实现文件

    - (NSDecimalNumber *)totalCost
    {
        return [self valueForKeyPath:@"InvoiceLineItems.@sum.cost"];
    }
    

    【讨论】:

      猜你喜欢
      • 2022-12-10
      • 1970-01-01
      • 2021-07-30
      • 1970-01-01
      • 2019-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多