3

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

- (void)setIncludesPendingChanges:(BOOL)yesNo

根据文档

YES不支持与结果类型一起使用的 值NSDictionaryResultType,包括聚合结果的计算(例如最大值和最小值)。对于字典,从 fetch 返回的数组反映了持久存储中的当前状态,并且不考虑上下文中的任何未决更改、插入或删除。如果您需要为一些简单的聚合(例如 max 和 min)考虑挂起的更改,则可以改用普通的 fetch 请求,按您想要的属性排序,获取限制为 1。

好的,我如何仍然包含待处理的更改?我正在使用 aNSFetchedResultsController来显示我的数据。这是我的聚合函数:

- (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];
}

从控制器DidChangeContent 调用。

今天就够了.. :-)

4

2 回答 2

4

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

NSSet *shortSet = [self.hasExpenditures filteredSetUsingPredicate:
  [NSPredicate predicateWithFormat:@"date > %@", startDate]];
NSNumber *total = [shortSet valueForKeyPath:@"@sum.amount"];
于 2013-12-30T22:56:05.957 回答
1

我完全不确定使用谓词过滤出一个子集是否比预先建议的原始循环更快。

这是一个更简洁和漂亮的代码,但绝不是更快。以下是我可以立即看到的一些原因(开销)。

  1. 从文本格式创建和编译谓词需要时间和内存(多次分配)
  2. 再次使用谓词过滤 self.hasExpenditures 分配并启动一个新的 NSSet (shortSet) 并用(保留的)对匹配支出(在日期范围内)的引用填充它。为此,它必须在循环中一一扫描 self.expenditures。
  3. 然后最后的总计算,再次循环遍历对数量求和的子集,并分配最终的 NSNumber 对象。

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

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

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

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

于 2014-01-01T07:03:44.817 回答