2009-03-04 73 views
9

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

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

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

在Invoice对象中,我有一个InvoiceLineItems的NSMutableArray。发票有一个类似的属性,称为totalCost。它是通过遍历行项目进行计算的,只要该行项目未标记为已删除(我为了同步原因所做的),它会累加成本并创建总成本。

现在我的问题/问题。如何设置发票的totalCost,以便在订单项的某个成本发生变化时与KVO /绑定一起使用?

我试着设置:

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

,但它不工作。我最终在控制台发生错误:[<NSCFArray 0x1499ff40> addObserver:forKeyPath:options:context:] is not supported. Key path: cost

回答

6

我不相信自动KVO传播支持许多关系。这些文档并不是以某种方式说明,而是从我所了解的KVO一般来看,观察一对多关系的子键往往是不平凡的。

我将接近这将是人工观察每个InvoiceLineItem对象的cost财产,通过实现对发票类的lineItems属性对多KVC访问器做在插入一个的addObserver/removeObserver调用的方法/删除方法,然后分别使用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"]; 
    } 
} 
+0

我是非常接近这种实现的自己,但它是很好听来自别人。问题是,will/didChange的堆叠...为什么不直接调用didChange? – zorn 2009-03-04 22:04:31

0

你可以尝试一个较短的解决方案。

加入头文件:

@property (retain, readonly) NSDecimalNumber *accountBalance; 

添加到实现文件

- (NSDecimalNumber *)totalCost 
{ 
    return [self valueForKeyPath:@"[email protected]"]; 
}