仅在参数更改时才在实例方法中处理代码

时间:2012-10-25 14:01:59

标签: objective-c ios oop class design-patterns

我有一些方法需要一些时间来处理以获取数据,执行计算然后返回结果,例如此实例方法基于三个参数返回一个数组:

-(NSArray*)periodsForCompanies:(NSArray*)companies figureType:(NSString*)figureType
actualValuesOnly:(BOOL)actualValuesOnly
    {.. };

由于此方法可以使用相同类中的相同参数多次调用并且需要一些时间才能完成,因此我希望优化我的代码以避免每次都完全执行此方法的代码。

如何将以前的参数(和/或结果)存储在方法中,以便能够将当前参数与之前的参数进行比较,并确定是否需要再次执行代码?在这种情况下,什么是“最佳做法”?

我的理解是,当调用方法时,通常方法中的所有变量都会重置为零和nil。

修改

根据要求,我添加了一个代码示例:

- (NSArray*)periodsForCompanies:(NSArray*)companies figureType:(NSString*)figureType actualValuesOnly:(BOOL)actualValuesOnly
{
    // fetch all periods

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"IBEstPeriod"];
    NSPredicate *predicate = [[NSPredicate alloc]init];

    if (actualValuesOnly == YES)
        predicate = [NSPredicate predicateWithFormat:@"(company IN %@) AND (ANY estType.actValue != nil) AND (ANY estType.type == %@)", self.companies, figureType];
    else
        predicate = [NSPredicate predicateWithFormat:@"(company IN %@) AND (ANY estType.type == %@)", self.companies, figureType];

    request.predicate = predicate;
    request.resultType = NSDictionaryResultType;
    request.returnsDistinctResults = YES;
    request.propertiesToFetch = @[@"endCalYear",@"endMonth",@"periodLength"];
    request.sortDescriptors =
        @[[NSSortDescriptor sortDescriptorWithKey:@"endCalYear"     ascending:NO],
        [NSSortDescriptor   sortDescriptorWithKey:@"endMonth"       ascending:NO],
        [NSSortDescriptor   sortDescriptorWithKey:@"periodLength"   ascending:NO]];

    NSError *fetchError = nil;
    NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&fetchError];

    NSMutableArray *distinctPeriods = results.mutableCopy;

    if (fetchError) {
        NSLog(@"Error during fetch request:%@", [fetchError localizedDescription]);
    } else {
        // NSLog(@"results: %@",results);
    }
    // remove periods for which not all companies have data for estimate type specified

    NSString const *endCalYearKey = @"endCalYear";
    NSString const *endMonthKey = @"endMonth";
    NSString const *periodLengthKey = @"periodLength";

    const NSIndexPath *yoyGrowthIndexPath =     [NSIndexPath indexPathForRow:0 inSection:0];
    const NSIndexPath *seqGrowthIndexPath =     [NSIndexPath indexPathForRow:1 inSection:0];
    const NSIndexPath *customGrowthIndexPath =  [NSIndexPath indexPathForRow:2 inSection:0];

    NSMutableIndexSet *indexesForPeriodsToRemove = [NSMutableIndexSet indexSet];

    [distinctPeriods enumerateObjectsUsingBlock:^(NSDictionary *estPeriodDict, NSUInteger idx, BOOL *stop) {

        NSNumber *endCalYear = estPeriodDict[endCalYearKey];
        NSNumber *endMonth = estPeriodDict[endMonthKey];
        NSNumber *periodLength = estPeriodDict[periodLengthKey];

        NSPredicate *predicate = [[NSPredicate alloc]init];
        UITableView *tableView = [self tableView];

        if ( [[tableView indexPathForSelectedRow] isEqual:customGrowthIndexPath] ) {

            // company predicate:

            predicate = [NSPredicate predicateWithFormat: @"ANY estimateType.type == %@ "
                         "AND SUBQUERY(estimatePeriod, $x, $x.endCalYear == %@ AND $x.endMonth == %@ AND $x.periodLength == %@ AND ANY $x.estType.type == %@).@count > 0",
                         figureType,
                         endCalYear, endMonth, periodLength, figureType];

        } else if ( [[tableView indexPathForSelectedRow] isEqual:yoyGrowthIndexPath] ) {

            predicate = [NSPredicate predicateWithFormat: @"ANY estimateType.type == %@ "
                         "AND SUBQUERY(estimatePeriod, $x, $x.endCalYear == %@ AND $x.endMonth == %@ AND $x.periodLength == %@ AND ANY $x.estType.type == %@).@count > 0"
                         "AND SUBQUERY(estimatePeriod, $x, $x.endCalYear == %i AND $x.endMonth == %@ AND $x.periodLength == %@ AND ANY $x.estType.type == %@).@count > 0",
                         figureType,
                         endCalYear, endMonth, periodLength, figureType,
                         endCalYear.integerValue - 1, endMonth, periodLength, figureType];

        } else if  ( [[tableView indexPathForSelectedRow] isEqual:seqGrowthIndexPath] ) {

            // TODO: rewrite
            predicate = [NSPredicate predicateWithFormat: @"ANY estimateType.type == %@ "
                         "AND SUBQUERY(estimatePeriod, $x, $x.endCalYear == %@ AND $x.endMonth == %@ AND $x.periodLength == %@ AND ANY $x.estType.type == %@).@count > 0",
                         figureType,
                         endCalYear, endMonth, periodLength, figureType];
        } else {
            [NSException raise:NSInternalInconsistencyException format:@"TableView: Invalid selection state in section 0 (NSIndexPath: %@)",super.tableView.indexPathForSelectedRow];
        }

        NSArray *companiesWithDataForPeriod = [self.companies filteredArrayUsingPredicate:predicate];
        NSLog(@"type: %@, period: %@/%@(%@), companies: %@", figureType, endCalYear, endMonth, periodLength,[companiesWithDataForPeriod valueForKey:@"contrSymbol"]);

        // mark periods which are not defined for all companies for removal (from display):
        if ( companiesWithDataForPeriod.count < self.companies.count ) [indexesForPeriodsToRemove addIndex:idx];

    }]; // end block

    [distinctPeriods removeObjectsAtIndexes:indexesForPeriodsToRemove];
    return distinctPeriods;
}

2 个答案:

答案 0 :(得分:1)

您需要进行某种缓存,实际上您的参数可能会有很大差异,因为您将公司数组作为参数。如果您使用大量组合调用此方法,则可能会导致大量内存。

我会改变它只通过一家公司,因此缓存会更小,而不是在最坏的情况下保留公司的所有组合。

要将数据保存在缓存中,您可以创建一些NSMutableDictionary属性或静态值(记得在某处释放/清空它),然后作为键,您可以将这些参数组合在某个对象中或作为简单字符串。

因此,您的方法的内部实现如下:

simpleString = .. combined arguments ...
NSArray *result = [dictionary objectForKey:simpleString]
if (result == nil)
{
    .. do fetch and put result in result
    [dictionary setObject:result forKey:simpleString];
}
return result;

答案 1 :(得分:1)

您想要缓存提取的结果。

假设您只想在运行时缓存结果,并在重新启动应用时重新获取结果。在这种情况下,请使用内部类和NSMutableDictionary。添加属性

@property (nonatomic, strong, readwrite) NSMutableDictionary *periodsForParameters;

将以下类添加到源(.m)文件中:

@interface PeriodRequestParameters : NSObject <NSCopying>
+ (PeriodRequestParameters *)parametersWithCompanies:(NSArray *)companies figureType:(NSString *)figureType actualValuesOnly:(BOOL)actualValuesOnly;
@property (nonatomic, strong, readwrite) NSArray *companies;
@property (nonatomic, strong, readwrite) NSString *figureType;
@property (nonatomic, assign, readwrite) BOOL actualValuesOnly;
- (BOOL)isEqualToPeriodRequestParameters:(PeriodRequestParameters *)comparand;
@end

@implementation PeriodRequestParameters

@synthesize companies, figureType, actualValuesOnly;

+ (PeriodRequestParameters *)parametersWithCompanies:(NSArray *)companies figureType:(NSString *)figureType actualValuesOnly:(BOOL)actualValuesOnly
{
    PeriodRequestParameters *res = [[PeriodRequestParameters alloc] init];
    res.companies = companies;
    res.figureType = figureType;
    res.actualValuesOnly = actualValuesOnly;
    return res;
}

- (BOOL)isEqualToPeriodRequestParameters:(PeriodRequestParameters *)comparand
{
    return ([self.companies isEqual:comparand.companies] &&
            [self.figureType isEqual:comparand.figureType] &&
            self.actualValuesOnly == comparand.actualValuesOnly);
}


// These methods are used by NSDictionary, so they must be overridden.
- (BOOL)isEqual:(id)object
{
    BOOL res = NO;
    if ([object isMemberOfClass:[PeriodRequestParameters class]]) {
        res = [self isEqualToPeriodRequestParameters:object];
    }
    return res;
}

- (NSUInteger)hash
{
    //This method can be made better, but this will work.
    return self.companies.hash;
}

//NSCopying

- (id)copyWithZone:(NSZone *)zone
{
    PeriodRequestParameters *res = [[PeriodRequestParameters allocWithZone:zone] init];
    res.companies = self.companies.copy;
    res.figureType = self.figureType.copy;
    res.actualValuesOnly = self.actualValuesOnly;
    return res;
}

@end

此类为您提供了一种方法,可以将periodsForCompanies:figureType:actualValuesOnly:中传递的参数用作periodsForParameters字典中的键。现在,在获取结果后,您可以添加已提取的数组作为键[PeriodRequestParameters parametersWithCompanies:companies figureType:figureType actualValuesOnly:actualValuesOnly]的对象,其中companiesfigureTypeactualValuesOnly是参数传递到periodForCompanies:figureType:actualValuesOnly:

然后,每次拨打periodForCompanies:figureType:actualValuesOnly:时,请先检查periodsForParameters缓存中是否有条目。

您可以使用NSDictionary作为periodsForParameters字典中的键,但在我看来,使用自定义类的代码看起来更清晰:您不必处理多个字符串检查是否有缓存结果或将新获取的结果添加到缓存时,键或手动构建字典。