配列から特定のデータを取り出す時や、いじるときに以下のようにループを回す場面は多々あります。
for (int i = 0; i < array.count; i++) {
;
}
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)opts に NSEnumerationConcurrent を指定することで、並列処理を行うことができます。
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
まとめ
使わない理由が見つかりません!