Skip to content

KVC: Set And Array Operators @min

2010.08.10

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

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


@min オペレータは、最小のものを探し出すためのオペレータです。最小のものは、compare: メソッドによって一番小さいと判断されたものになります。

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

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

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

NSNumber *minOfAge;
minOfAge = [company valueForKeyPath:@"employees.@min.age"];

上の例で、minOfAge は 20 となります。

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

NSNumber *minOfAge = nil;
NSArray *ages;
ages = [company valueForKeyPath:@"employees.age"];
for (NSNumber *age in ages) {
    if (!minOfAge || [age compare:minOfAge] == NSOrderedAscending) {
        minOfAge = age;
    }
}

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

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

@implementation minAppDelegate

- (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];
    
    // @min version
    sumOfTime = 0.0;
    for (int i = 0; i < count; i++) {
        // Make pool
        NSAutoreleasePool *pool;
        pool = [[NSAutoreleasePool alloc] init];
        
        // Record startTime
        startTime = CFAbsoluteTimeGetCurrent();
        
        // Get minOfAge using @min
        NSNumber *minOfAge;
        minOfAge = [company valueForKeyPath:@"employees.@min.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 minOfAge manually
        NSNumber *minOfAge = nil;
        NSArray *ages;
        ages = [company valueForKeyPath:@"employees.age"];
        for (NSNumber *age in ages) {
            if (!minOfAge
                || [age compare:minOfAge] == NSOrderedAscending
            ){
                minOfAge = age;
            }
        }
        
        // Update sumOfTime
        sumOfTime += (CFAbsoluteTimeGetCurrent() - startTime);
        
        // Release pool
        [pool release];
    }
    NSLog(@"time average = %f", sumOfTime / (double)count);
}

@end

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

@min version 0.002896 s
Manual version 0.002131 s

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

@min オペレータは、compare: メソッドによって最小のものを探し出すので、NSNumber に限らず、compare: メソッドを実装している NSString や NSDate の最小も探し出すことができます。もちろん、自分で作ったクラスで compare: メソッドを実装したときも、@min オペレータを使うことができます。
以下の例は、上記 company ディクショナリの中から最小の name を探し出しています。

NSString *minOfName;
minOfName = [company valueForKeyPath:@"employees.@min.name"];

上の例で、minOfName は “A” となります。

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

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