Skip to content

KVC: Set And Array Operators @sum

2010.08.04

KVC (Key-Value Coding) の Set And Array Operators をマスターすると、コードを簡素化できたり、NSPredicate でできることの幅が増えたり、Cocoa Bindings でできることの幅が増えるなどの利点があります。Set And Array Operators の概要から始まったシリーズ。

今回は、@sum オペレータを取り上げます。


@sum オペレータは、合計値を演算するためのオペレータです。valueForKeyPath: で得られる値を double に変換し、その合計を NSNumber で返します。

例えば、次のような NSDictionary 型変数 company があるとします。

company = {
    employees = (
        {
            age = 20;
            name = A;
        },
        {
            age = 23;
            name = B;
        },
        {
            age = 20;
            name = C;
        }
    );
}

このとき、age の合計は次のように求めることができます。

NSNumber *sumOfAge;
sumOfAge = [company valueForKeyPath:@"employees.@sum.age"];

上の例で、sumOfAge は 20 + 23 + 20 で 63 となります。

@sum オペレータを使わずに age の合計値を求める場合、コードは以下のようになります。

double sumOfAge = 0.0;
NSArray *ages;
ages = [company valueForKeyPath:@"employees.age"];
for (NSNumber *age in ages) {
    sumOfAge += [age doubleValue];
}

@sum オペレータを使用した方が、簡潔に結果を求めることができます。

パフォーマンスはどうでしょうか。比較してみました。
比較には以下のコードを使用しました。使用した機種は MacBook Pro。Mac アプリとして作成し、実行しました。

@implementation sumAppDelegate

- (NSDictionary*)company
{
    // Make employees
    NSMutableArray *employees;
    NSDictionary *employee;
    employees = [NSMutableArray array];
    for (NSUInteger i = 0; i < 10000; i++) {
        employee = [NSDictionary
            dictionaryWithObject:[NSNumber numberWithUnsignedInt:arc4random()]
            forKey:@"age"];
        [employees addObject:employee];
    }
    
    // Make company
    NSDictionary *company;
    company = [NSDictionary
        dictionaryWithObject:employees
        forKey:@"employees"];
    
    return company;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    CFAbsoluteTime startTime;
    CFTimeInterval sumOfTime;
    NSUInteger const count = 100;
    
    // Get company
    NSDictionary *company;
    company = [self company];
    
    // @sum version
    sumOfTime = 0.0;
    for (int i = 0; i < count; i++) {
        // Make pool
        NSAutoreleasePool *pool;
        pool = [[NSAutoreleasePool alloc] init];
        
        // Record startTime
        startTime = CFAbsoluteTimeGetCurrent();
        
        // Get sumOfAge using @sum
        NSNumber *sumOfAge;
        sumOfAge = [company valueForKeyPath:@"employees.@sum.age"];
        
        // Update sumOfTime
        sumOfTime += (CFAbsoluteTimeGetCurrent() - startTime);
        
        // Release pool
        [pool release];
    }
    NSLog(@"time average = %f", sumOfTime / (double)count);
    
    // Manual version
    sumOfTime = 0.0;
    for (int i = 0; i < count; i++) {
        // Make pool
        NSAutoreleasePool *pool;
        pool = [[NSAutoreleasePool alloc] init];
        
        // Record startTime
        startTime = CFAbsoluteTimeGetCurrent();
        
        // Get sumOfAge manually
        double sumOfAge = 0.0;
        NSArray *ages;
        ages = [company valueForKeyPath:@"employees.age"];
        for (NSNumber *age in ages) {
            sumOfAge += [age doubleValue];
        }
        
        // Update sumOfTime
        sumOfTime += (CFAbsoluteTimeGetCurrent() - startTime);
        
        // Release pool
        [pool release];
    }
    NSLog(@"time average = %f", sumOfTime / (double)count);
}

@end

測定結果は以下の通りになりました (3回の平均):

@sum version 0.048518 s
Manual version 0.001465 s

@sum オペレータを使用すると速度は遅くなる、という結果が出ました。@sum オペレータを使用すべきかどうかは、ケース・バイ・ケースということですね。

次回は @min オペレータを取り上げます。

Advertisements

From → Develop

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s