koogawa blog

iOS、Android、foursquareに関する話題

Objective-Cのいろいろな反復処理

本日、iOS_LTというイベントで、Objective-Cで利用できるいろいろな反復処理について発表してきました。

発表内容

例えばこんな配列と辞書があるとします。

    NSArray *anArray = @[@"a", @"b", @"c"];
    NSDictionary *aDictionary = @{@"key1": @"val1",
                                  @"key2": @"val2",
                                  @"key3": @"val3"};

これらの要素を反復処理で順に処理することを考えます。

for ループで回す

一番基本的な方法はこれでしょう。

    for (int i = 0; i < anArray.count; i++)
    {
        id object = anArray[i];
        NSLog(@"object = %@", object);
    }
    NSArray *keys = [aDictionary allKeys];
    for (int i = 0; i < keys.count; i++)
    {
        id key = keys[i];
        id value = aDictionary[key];
        NSLog(@"key = %@, value = %@", key, value);
    }

for ループを使った場合は、変数 i で簡単にインデックスを取得できる、といったメリットがあります。

しかし、辞書の場合はコードを見ても分かる通り、最初にすべてのキーを取り出したり、ループごとに毎回一時変数を作る必要があります。

高速反復処理

高速反復処理を使うことで、ソースコードがだいぶシンプルになります。

    for (id object in anArray)
    {
        NSLog(@"object = %@", object);
    }
    for (id key in aDictionary)
    {
        id value = aDictionary[key];
        NSLog(@"key = %@, value = %@", key, value);
    }

しかし、for ループの場合とは違い、インデックスにアクセスできないというデメリットもあります。また、辞書の場合は value を取り出すための余分なステップが必要になります。

ブロックベースの反復処理

for ループと高速反復処理の両方のメリットを持った方法がこちらです。

    [anArray enumerateObjectsUsingBlock:
     ^(NSString *object, NSUInteger idx, BOOL *stop) {
         NSLog(@"object = %@, idx = %lu", object, (unsigned long)idx);
     }];
    [aDictionary enumerateKeysAndObjectsUsingBlock:
     ^(id key, id object, BOOL *stop) {
         NSLog(@"key = %@, object = %@", key, object);
    }];

このメソッドを使うことで、 keyvalue が同時に手に入り、さらにはインデックスにもアクセスすることが可能です。ループを止めたい場合は、変数 stopYES をセットします。

Options を指定することで、逆順に処理を回すこともできます。

    [anArray enumerateObjectsWithOptions:NSEnumerationReverse
                              usingBlock:
     ^(NSString *object, NSUInteger idx, BOOL *stop) {
         NSLog(@"object = %@, idx = %lu", object, (unsigned long)idx);
     }];

メソッド名が多少長いことを除けば、最も良い方法ではないでしょうか。

気になるところ

  • それぞれの処理速度にどの程度差が出るのか

参考書籍

この記事は、「項目48 forループではなく、ブロックの反復処理を使う」を参考にして書きました。

リンク

今回のサンプルコードは Gist に上げてあります。

2014.3.13追記

id:quesera2 さんによる記事 Objective-Cの列挙の話。 - なるようになるかも がとても参考になります。列挙に対する理解が更に深まりました。