Skip to content

KVC: Set And Array Operators @max

2010.08.14

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

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


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

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

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

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

NSNumber *maxOfAge;
maxOfAge = [company valueForKeyPath:@"employees.@max.age"];

上の例で、maxOfAge は 23 となります。

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

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

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

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

@implementation maxAppDelegate

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

@end

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

@max version 0.002180 s
Manual version 0.002235 s

@max オペレータを使用した方が速度が若干速くなる、という結果が出ました。@max オペレータは普段使いしてもよさそうです。

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

NSString *maxOfName;
maxOfName = [company valueForKeyPath:@"employees.@max.name"];

上の例で、maxOfName は “C” となります。

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

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