本日、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); }];
このメソッドを使うことで、 key
と value
が同時に手に入り、さらにはインデックスにもアクセスすることが可能です。ループを止めたい場合は、変数 stop
に YES
をセットします。
Options
を指定することで、逆順に処理を回すこともできます。
[anArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock: ^(NSString *object, NSUInteger idx, BOOL *stop) { NSLog(@"object = %@, idx = %lu", object, (unsigned long)idx); }];
メソッド名が多少長いことを除けば、最も良い方法ではないでしょうか。
気になるところ
- それぞれの処理速度にどの程度差が出るのか
参考書籍
この記事は、「項目48 forループではなく、ブロックの反復処理を使う」を参考にして書きました。
リンク
- objective c - When to use enumerateObjectsUsingBlock vs. for - Stack Overflow
- Objective-Cの列挙の話。 - なるようになるかも
今回のサンプルコードは Gist に上げてあります。
2014.3.13追記
id:quesera2 さんによる記事 Objective-Cの列挙の話。 - なるようになるかも がとても参考になります。列挙に対する理解が更に深まりました。