【发布时间】:2013-12-30 16:24:33
【问题描述】:
我终于至少缩小了这个问题的范围。我正在计算一些支出的聚合函数(如本例中的总和)。如果我更改了一些支出,则此聚合获取不会立即刷新,而是会在一段时间后刷新(可能是在将更改保存到数据库之后)。我在文档中找到了这部分:
- (void)setIncludesPendingChanges:(BOOL)yesNo
根据文档
不支持将值
YES与结果类型结合使用NSDictionaryResultType,包括汇总结果的计算 (例如最大值和最小值)。对于字典,从返回的数组 fetch 反映持久存储中的当前状态,而不是 考虑到任何待处理的更改、插入或删除 语境。如果您需要考虑一些待处理的更改 像 max 和 min 这样的简单聚合,你可以使用普通的 获取请求,按您想要的属性排序,获取限制为 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];
}
从 controllerDidChangeContent 调用。
今天就够了.. :-)
【问题讨论】:
-
恐怕你不能(因为
NSDictionaryResultType暗示setIncludesPendingChanges:NO)。进行更改后,您必须明确保存上下文。 -
您可以随时保存上下文,唯一的问题是保存通常会使您的应用程序变慢。 - NSExpressionDescription 与显式循环相比的优势在于 NSExpressionDescription 在 SQLite 级别上执行,无需将所有对象提取到内存中(引发故障)。但我没有总体上更好的建议,您可能需要进行一些性能测试。
-
完全同意@MartinR。无论如何,如果您不想保存,循环是正确的选择。好吧,你应该按照马丁的建议做一些措施。同步意味着你需要等待保存结束,然后再调用你感兴趣的方法。所以,做一个保存,然后调用更新方法。
-
@MichiZH:是的。您可以设置启动参数
-com.apple.CoreData.SQLDebug 1(或3)以在执行所有 SQLite 命令时获取有关它们的调试输出。 -
Objective-C 中没有运算符重载,请使用
NSDate的compare:方法(比较该类型的日期)
标签: ios objective-c core-data nsfetchedresultscontroller nsexpression