Skip to content

KVC: Set And Array Operators @count

2010.08.22

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

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


@count オペレータは、オブジェクトの数を演算するためのオペレータです。オブジェクトの数は、NSNumber で返されます。@count オペレータ以降のキーパスは無視されます。

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

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

このとき、employees の要素数は、次のように求めることができます。

NSNumber *count;
count = [company valueForKeyPath:@"employees.@count"];

上の例で、count は、3 となります。

@count オペレータを使わずに employees の要素数を求める場合、コードは以下のようになります。

NSUInteger count;
count = [[company valueForKey:@"employees"] count];

@count オペレータは、使っても使わなくても、コード量はさほど変わりありません。

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

@implementation countAppDelegate

- (NSDictionary*)company
{
    // Make employees
    NSMutableArray *employees;
    NSDictionary *employee;
    employees = [NSMutableArray array];
    for (NSUInteger i = 0; i < 10000; i++) {
        employee = [NSDictionary
            dictionaryWithObject:@"Name"
            forKey:@"name"];
        [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];
    
    // @count version
    sumOfTime = 0.0;
    for (int i = 0; i < count; i++) {
        // Make pool
        NSAutoreleasePool *pool;
        pool = [[NSAutoreleasePool alloc] init];
        
        // Record startTime
        startTime = CFAbsoluteTimeGetCurrent();
        
        // Count employees using @count
        NSNumber *count;
        count = [company valueForKeyPath:@"employees.@count"];
        
        // 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();
        
        // Count employees manually
        NSUInteger count;
        count = [[company valueForKey:@"employees"] count];
        
        // Update sumOfTime
        sumOfTime += (CFAbsoluteTimeGetCurrent() - startTime);
        
        // Release pool
        [pool release];
    }
    NSLog(@"time average = %f", sumOfTime / (double)count);
}

@end

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

@count version 0.000002 s
Manual version 0.000000 s

@count オペレータを使用した方が速度が遅くなる、という結果が出ました。

@count オペレータを使用しても、コード量を減らせるわけではない、パフォーマンスが良くなるわけでもない、という結果になりました。一見、なんのメリットもなさそうに見えます。しかし、NSPredicate や Cocoa Bindings では、その威力を発揮します。例えば、NSPredicate では「なになにの数が X」という条件を作ることができるようになります。また、Cocoa Bindings では、「なになにの数が 0 ならば、あるボタンを無効化する」というバインディングを作ることができるようになります。なので、@count オペレータの使い方は、マスターしておいて損はありません。

次回は、@unionOfObjects を取り上げます。

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