Настройка KVO для рассчитанных значений, основанных на рассчитанных значениях
-
03-07-2019 - |
Вопрос
Итак, у меня есть два объекта, Invoice и InvoiceLineItem. InvoiceLineItem имеет свойство с именем cost
и динамически создается на основе других свойств. Чтобы помочь с КВО / привязками, которые я использую:
+ (NSSet *)keyPathsForValuesAffectingCost {
return [NSSet setWithObjects:@"lineItemType", @"serviceCost", @"hourlyRate", @"timeInSeconds", @"productCost", @"quantityOfProduct", @"mileageCost", @"milesTraveled", nil];
}
Это прекрасно работает. Когда я редактирую свойство, такое как serivceCost, основная стоимость в представлении таблицы обновляется нормально.
В объекте Invoice у меня есть NSMutableArray из InvoiceLineItems. Счет-фактура имеет похожее свойство с именем totalCost
. Он рассчитывается путем итерации по отдельным позициям и до тех пор, пока позиция не помечается как удаленная (что я делаю по причинам синхронизации), она складывает затраты и создает totalCost.
Теперь мой вопрос / проблема. Как настроить Invoice's totalCost, чтобы он работал с KVO / привязками, когда изменилась стоимость одной позиции?
Я попытался настроить:
+ (NSSet *)keyPathsForValuesAffectingTotalCost {
return [NSSet setWithObjects:@"lineItems.cost", nil];
}
но это не работает. Я получаю сообщение об ошибке в консоли: [<NSCFArray 0x1499ff40> addObserver:forKeyPath:options:context:] is not supported. Key path: cost
Решение
Я не верю, что многие отношения поддерживаются для автоматического распространения KVO. Документация не говорит об ясности так или иначе, но из того, что я знаю о KVO в целом, наблюдение за подразделами отношения ко многим имеет тенденцию быть нетривиальным.
Я хотел бы подойти к этому вручную, наблюдая свойство cost
каждого объекта InvoiceLineItem, реализуя многие методы доступа KVC для свойства lineItems
класса Invoice, выполняя вызов addObserver / removeObserver в соответственно вставьте / удалите методы, а затем запустите изменение totalCost
вручную, используя willChangeValueForKey: / didChangeValueForKey :. Так что-то вроде этого (примерный набросок кода, отказ от ответственности и т. Д.):
- (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"];
}
}
Другие советы
Вы можете попробовать более короткое решение.
Добавить в заголовочный файл:
@property (retain, readonly) NSDecimalNumber *accountBalance;
Добавить в файл реализации
- (NSDecimalNumber *)totalCost
{
return [self valueForKeyPath:@"InvoiceLineItems.@sum.cost"];
}