Help us understand the problem. What is going on with this article?

NSArrayでfor(; ;)とかfor-inを使うのをやめて、enumerateObjectsUsingBlock:を使う

More than 5 years have passed since last update.

配列から特定のデータを取り出す時や、いじるときに以下のようにループを回す場面は多々あります。

example1
for (int i = 0; i < array.count; i++) {
    ;
}
example2
for (id object in array) {
    ;
}

example1ではループの際に毎回array.countが呼び出されて無駄だし、条件式などあるのでバグも生みやすい。
example2ではexample1のiに相当する配列のインデックスを求めるためには、変数を別途作ってインクリメントする必要があり面倒です。

そんな話をしていたところ、友人にNSArrayにはenumerateObjectsUsingBlock:なる便利なメソッドが存在することを教えてもらったのでご紹介します。

enumerateObjectsUsingBlock:を使う

enumerateObjectsUsingBlock:はNSArrayに用意されているインスタンスメソッドで、iOS 4.0以上で使用することができます。

NSArray Class Reference - enumerateObjectsUsingBlock:

定義
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
  • block
    • enumerateObjectsUsingBlock:はBlockの中に配列にしたい操作を記述します。Blockの中がfor(;;){ hoge }のhogeに相当します。
  • obj
    • 配列から取り出された要素です。
  • idx
    • 配列の要素のインデックスです。example1のiに相当します。
  • stop
    • 停止フラグです。stopにYES(BOOL)を入れるとその時点で停止します。forのbreakに相当します。

 

使い方
/*
    NSStringの配列から@"c"が入っている要素番号を取得する
*/

NSArray *array = @[@"a", @"b", @"c", @"d"];

__block NSInteger result = -1;  // オブジェクト型でない変数をBlock内で操作する場合は__block修飾子が必要

[array enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {
    NSLog(@"%d: %@", idx, obj);

    if ([obj isEqualToString:@"c"]) {
        result = idx;
        *stop = YES;
    }   
}];

NSLog(@"result: %d", result);



// 出力結果
0: a
1: b
2: c
result: 2

使いやすいです。

enumerateObjectsWithOptions:usingBlock:を使うと要素を後ろから見れます

配列を後ろから見ていきたいときに便利です。

NSArray Class Reference - enumerateObjectsWithOptions:usingBlock:

定義
- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block

enum {
   NSEnumerationConcurrent = (1UL << 0),
   NSEnumerationReverse = (1UL << 1),
};
typedef NSUInteger NSEnumerationOptions;
  • opts
    • NSEnumerationReverse 後ろから見ていきます
使い方
NSArray *array = @[@"a", @"b", @"c", @"d"];

[array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {
    NSLog(@"%d: %@", idx, obj);
}];


// 出力結果
3: d
2: c
1: b
0: a

処理の並列化もできます

上記の (NSEnumerationOptions)optsNSEnumerationConcurrent を指定することで、並列処理を行うことができます。

使い方
NSArray *array = @[@"a", @"b", @"c", @"d"];

[array enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {
    NSLog(@"%d: %@", idx, obj);
}];


// 出力結果
3: d
1: b
2: c
0: a

速度はforと同等かそれ以上

気になる実行速度ですが、for(;;)やfor-inと同等か、それ以上みたいなので安心して使えます。

objective c - When to use enumerateObjectsUsingBlock vs. for - Stack Overflow

まとめ

使わない理由が見つかりません!

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away