iOS对聚合函数的FetchRequest:如何包括挂起的更改



我终于缩小了这个问题的范围。我正在计算一些支出的聚合函数(如本例中的总和)。如果我更改了一些支出,则此聚合获取不会立即刷新,而是仅在一段时间后刷新(可能是在更改保存到数据库之后)。我在文档中找到了这个部分:

- (void)setIncludesPendingChanges:(BOOL)yesNo

根据文件

YES的值不支持与结果类型结合使用NSDictionaryResultType,包括汇总结果的计算(例如最大值和最小值)。对于字典,从fetch反映持久存储中的当前状态,而不是考虑中任何挂起的更改、插入或删除上下文如果您需要考虑一些挂起的更改简单的聚合,如max和min,可以使用法线fetch请求,根据所需属性排序,fetch限制为1.

好的,我怎么还能包括挂起的更改?我正在使用NSFetchedResultsController来显示我的数据。这是我的聚合函数:

- (NSNumber *)getExpendituresAmountForCostPeriod:(CostPeriod)costPeriod
{
NSLog(@"getExpenditures_Start");
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Expenditures"];
[fetchRequest setResultType:NSDictionaryResultType];
NSDate *startDate = [NSDate startDateForCostPeriod:[self getBiggestCostPeriod]];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"forSpendingCategory = %@ AND date >= %@", self, startDate];        
//Define what we want
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: @"amount"];
NSExpression *sumExpression = [NSExpression expressionForFunction: @"sum:"
arguments: [NSArray arrayWithObject:keyPathExpression]];
//Defining the result type (name etc.)
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: @"totalExpenditures"];
[expressionDescription setExpression: sumExpression];
[expressionDescription setExpressionResultType: NSDoubleAttributeType];
// Set the request's properties to fetch just the property represented by the expressions.
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
NSLog(@"%@", self.managedObjectContext);
// Execute the fetch.
NSError *error = nil;
NSArray *objects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (objects == nil) {
return [NSNumber numberWithDouble:0];
} else {
if ([objects count] > 0) {
return [[objects objectAtIndex:0] valueForKey:@"totalExpenditures"];
} else {
return [NSNumber numberWithDouble:0];
}
}
}

编辑:*通过NSSet的循环是否可能且足够快*

- (NSNumber *)getExpendituresAmountForCostPeriod:(CostPeriod)costPeriod
{
NSDate *startDate = [NSDate startDateForCostPeriod:[self getBiggestCostPeriod]];
double total = 0;
for(Expenditures *expenditure in self.hasExpenditures){
if(expenditure.date >= startDate){
total = total + [expenditure.amount doubleValue];
}
}
return [NSNumber numberWithDouble:total];
}

最后用答案编辑感谢大家,我终于发现了循环中的问题。这工作非常快和好:

- (NSNumber *)getExpendituresAmountForCostPeriod:(CostPeriod)costPeriod
{
NSDate *startDate = [NSDate startDateForCostPeriod:[self getBiggestCostPeriod]];
double total = 0;
for(Expenditures *expenditure in self.hasExpenditures){
if([expenditure.date compare: startDate] == NSOrderedDescending){
total = total + [expenditure.amount doubleValue];
}
}
return [NSNumber numberWithDouble:total];
}

从controllerIdChangeContent调用。

今天就够了..:-)

您的解决方案还可以,但您仍然可以通过首先缩短集合,然后利用KVC:避免循环来加快速度并生成更短的代码

NSSet *shortSet = [self.hasExpenditures filteredSetUsingPredicate:
[NSPredicate predicateWithFormat:@"date > %@", startDate]];
NSNumber *total = [shortSet valueForKeyPath:@"@sum.amount"];

我根本不确定使用谓词过滤出子集是否比之前建议的基元循环更快。

这是一个更简洁、更漂亮的代码,但绝不会更快。以下是我马上就能明白的几个原因(管理费用)。

  1. 从文本格式创建和编译谓词需要时间和内存(多次分配)
  2. 再次使用谓词筛选self.has支出会分配并启动一个新的NSSet(shortSet),并用对匹配支出的(保留的)引用填充它(带有日期范围)。为此,它必须在循环中一个接一个地扫描self.expendities
  3. 然后进行最后一次总计算,再次在子集上循环求和,并分配最终的NSNumber对象

在循环版本中——没有新的分配,没有保留或释放任何东西,只有一次通过self.expenditures集。

总而言之,我的观点是,无论如何,第二个实现至少需要完成该循环的内容,再加上一些额外的开销。

最后一点:集合中的for id可以在多个项目上使用GCD同时运行,因此速度相当快。

我认为你至少应该尝试通过广泛的性能测试来匹配这些替代方案。

最新更新