Configuration du KVO pour les valeurs calculées, en fonction des valeurs calculées
-
03-07-2019 - |
Question
J'ai donc deux objets, Invoice et InvoiceLineItem. InvoiceLineItem a une propriété appelée cost
et elle est créée dynamiquement en fonction d'autres propriétés. Pour aider avec les liaisons KVO / I que j'utilise:
+ (NSSet *)keyPathsForValuesAffectingCost {
return [NSSet setWithObjects:@"lineItemType", @"serviceCost", @"hourlyRate", @"timeInSeconds", @"productCost", @"quantityOfProduct", @"mileageCost", @"milesTraveled", nil];
}
Cela fonctionne très bien. Lorsque je modifie une propriété telle que serivceCost, le coût principal dans les mises à jour de la vue tableau est correct.
Dans l'objet Facture, j'ai un NSMutableArray de InvoiceLineItems. La facture a une propriété similaire appelée totalCost
. Il est calculé en effectuant une itération sur les éléments de campagne et tant que ce dernier n'est pas marqué comme supprimé (ce que je fais pour des raisons de synchronisation), il ajoute les coûts et crée le totalCost.
Maintenant ma question / problème. Comment configurer le totalCost de Invoice pour qu'il fonctionne avec les liaisons KVO / bind lorsque l'un des coûts de l'élément de ligne a changé?
J'ai essayé de configurer:
+ (NSSet *)keyPathsForValuesAffectingTotalCost {
return [NSSet setWithObjects:@"lineItems.cost", nil];
}
mais ça ne marche pas. Je me retrouve avec une erreur dans la console: [<NSCFArray 0x1499ff40> addObserver:forKeyPath:options:context:] is not supported. Key path: cost
La solution
Je ne crois pas que beaucoup de relations soient prises en charge pour la propagation automatique de KVO. La documentation ne dit pas explicitement d'une manière ou d'une autre, mais d'après ce que je sais du KVO en général, observer les sous-clés d'une relation à plusieurs tend à ne pas être trivial.
Pour y remédier, il serait utile d’observer manuellement la propriété cost
de chaque objet InvoiceLineItem, en implémentant plusieurs accesseurs KVC pour la propriété lineItems
de la classe Invoice lors d’un appel addObserver / removeObserver. méthodes d'insertion / suppression, respectivement, puis déclenchez la totalCost
modification manuellement à l'aide de willChangeValueForKey: / didChangeValueForKey :. Donc, quelque chose comme ceci (code grossièrement esquissé, disclaimers, etc.):
- (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"];
}
}
Autres conseils
Vous pouvez essayer une solution plus courte.
Ajouter au fichier d'en-tête:
@property (retain, readonly) NSDecimalNumber *accountBalance;
Ajouter au fichier d'implémentation
- (NSDecimalNumber *)totalCost
{
return [self valueForKeyPath:@"InvoiceLineItems.@sum.cost"];
}