Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
326
Help us understand the problem. What is going on with this article?
@exilias

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

まとめ

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

326
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  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
exilias
iPhoneアプリとかAndroidアプリ作ってます。
beartail
急成長中のクラウド経費精算システム「Dr.経費精算」を開発しています。ミッションは「時間革命で体感寿命を延ばす」

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
326
Help us understand the problem. What is going on with this article?