配列要素にアクセスするとき、Cでは要素数を自分で管理する必要があった。
要素数を超えてアクセスしても言語側は知らんぷりなので、暴走の原因になることもしばしばである。
(高速化などトリックにも使えるのだが、それは上級者のみに許された究極の技。)
そもそも、要素数をユーザーが管理する必要がある。
しかし、Cocoaのクラスでは要素数をシステムが管理してくれるので、範囲を超えて越えてアクセスすると
例外がかかり止まる。また、要素数は[count]メッセージで取得出来る。
そのため、安全(要素個数を超えることなく)にループを回すことが出来る。
要素番号が必要な場合はこのループを使う。
NSUInteger i; // 要素番号 NSArray *array = [NSArray arrayWithObjects:@"abc", @"def", @"ghi", @"jkl", nil]; for (i = 0; i < [array count]; i++) { NSLog(@"index: %d, value: %@\n", i, [array objectAtIndex:i]); }
Objective-Cでは「列挙子」というものを使ってループを構築することも出来る。
これは要素の終端を判定してループを構築するもので、要素番号を保持しておく必要がなくなる
(逆に言えば、ループ内では要素番号を参照することはそのままでは出来ない)。
これにはNSEnumeratorとwhileループを使う。
NSArray *array = [NSArray arrayWithObjects:@"abc", @"def", @"ghi", @"jkl", nil]; NSEnumerator *enumerator = [array objectEnumerator]; id obj; while (obj = [enumerator nextObject]) { // nilで終了 NSLog(@"value: %@\n", obj); } // enumeratorは解放の必要がない(というかしたら駄目)objectEnumeratorの部分をreverseObjectEnumeratorにすると、配列の終端から先頭=0に向けての逆順になる。
例はNSArrayに対するものであるが、NSDictionaryでも同様な処理がかける。
Objective-C 2.0ではさらにfor文が拡張され、配列・辞書の内容が高速かつ安全に「参照」出来る様になっている。
高速列挙子である。ここで言う「高速」はコンパイラが最適化したコードを発生するという意味であり、
「安全」は終了判定を記述する必要がないことを意味する。
NSArray *array = [NSArray arrayWithObjects:@"abc", @"def", @"ghi", @"jkl", nil]; for (id obj in array) { // 新しいクラス変数を作り、上記arrayの内容を参照する(最後まで) NSLog(@"value: %@\n", obj); }
(高速)列挙子を使ったループをMutableで構築する場合、1つ注意がある。
それは、ループ内で要素の更新をしてはいけないことである。
NSMutaleArray *marray = [NSMutableArray arrayWithObjects:@"abc", @"def", @"ghi", @"jkl", nil]; NSEnumerator *enumerator = [marray objectEnumerator]; id obj; NSInterger i=0; while (obj = [enumerator nextObject]) { // nilで終了 NSLog(@"value: %@\n", obj); ~ [marray replaceObjectAtIndex:i withObject:obj]; i++; }とすると、例外が発生する。replaceObjectAtIndexのように、配列要素数が変化しない場合でも
同様である。要素を変更する場合は、素直に要素番号を持ってループを回すしかない。
0 件のコメント:
コメントを投稿