0

データをフェッチし、計算を実行してから結果を返すために処理に時間がかかるメソッドがいくつかあります。たとえば、このインスタンス メソッドは 3 つのパラメーターに基づいて配列を返します。

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

2 に答える 2

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]ここcompaniesで、、、figureTypeおよびactualValuesOnlyはに渡される引数periodForCompanies:figureType:actualValuesOnly:です。

次に、を呼び出すたびに、最初にキャッシュperiodForCompanies:figureType:actualValuesOnly:にエントリがすでに存在するかどうかを確認します。periodsForParameters

NSDictionary辞書のキーとしてを使用することもできますperiodsForParametersが、私の意見では、カスタムクラスを使用するコードははるかにクリーンに見えます。キーとして複数の文字列を処理したり、辞書があるかどうかを確認するときに手動で辞書を作成したりする必要はありません。キャッシュされた結果、または新しくフェッチされた結果をキャッシュに追加する場合。

于 2012-10-25T14:33:51.327 に答える
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;
于 2012-10-25T14:31:45.207 に答える