NSEnumerator で for...in 構文に対応させる : Objective-C プログラミング

PROGRAM


NSEnumerator で for...in 構文に対応させる

Objective-C のクラスを for ... in 構文に対応させる方法としては、クラスを NSFastEnumeration プロトコルに準拠させる方法の他にも、for ... in 構文に対応ししている NSEnumerator クラスのインスタンスをプロパティ等で返す方法があります。

 

NSFastEnumeration の場合、for ... in 構文で取得できるリストを 1 つのクラスに対して 1 種類しか実装できませんが、NSEnumerator インスタンスを返す方法を採れば、何種類でもリストを返すことができます。

たとえば NSArray では、先頭から順番に要素を取得できる機能を NSFastEnumeration で取得できますけど、それとは別に、要素を逆順に取得できる NSEnumerator を reverseObjectEnumerator プロパティを使って取得できるようになっています。

 

この NSEnumerator クラスは、既定では何も実装されていないので、実際に使用する場合はあらかじめ NSEnumerator から派生させたクラスを用意して、プロパティからは派生したクラスのインスタンスを返すようにする必要があります。

たとえば、インスタンス変数で保持している NSArray 型の配列から奇数番目の要素を順に取り出す oddEnumerator を実装する場合について見てみます。

実装

最終的に NSEnumerator* 型を返せば for ... in 構文で問題なく利用できるので、まず、Objective-C クラスのヘッダーでは NSEnumerator* を返すプロパティとして oddEnumerator を定義します。

@interface EzSampleList : NSObject

 

// ヘッダーでは NSEnumerator* 型のプロパティとして定義します。

@property (readonly,strong) NSEnumerator* oddEnumerator;

 

@end

ただし NSEnumerator そのものでは空のリストしか返せないので、適切なリストを返せるクラスを自分で作成する必要があります。

この、適切なリストを返せるクラスは NSEnumerator クラスの派生クラスとして作成します。

このクラスのインスタンス生成など、直接的な操作は全て oddEnumerator を実装しているクラス内でだけ行われるので、このクラスの定義と実装は、実装ファイル内(.m)で行ってしまえば大丈夫です。

// 独自の Enumerator は外部から直接操作しないため、定義は実装ファイル内(.m)で行ってしまって大丈夫です。

@interface EzSampleListOddEnumerator : NSEnumerator

 

- (id)initWithList:(NSArray*)list;

 

@end

 

// 実装ファイル内(.m)なので、実装も続けて行います。

@implementation EzSampleListOddEnumerator

{

__weak NSArray* _list;

NSUInteger _nextItemIndex;

}

 

- (id)initWithList:(NSArray *)list

{

// リストを受け取ってクラスを構築します。

self = [super init];

 

if (self)

{

_list = list;

}

 

return self;

}

 

- (id)nextObject

{

// リストの要素を奇数ごと(インデックス番号で見れば偶数ごと)に順に取り出すメソッドです。最後まで取り出したら nil を返します。

id result;

 

if (_nextItemIndex < _list.count)

{

result = _list[_nextItemIndex];

 

_nextItemIndex += 2;

}

else

{

result = nil;

}

 

return result;

}

 

@end

 

// Enumerator の定義が終わり、それを使うクラスの実装へと続きます。

@implementation EzSampleList

 

 

 

NSEnumerator には、要素を順番に取り出す nextObject メソッドの他にも、列挙するすべてのオブジェクトを NSArray* 型で取り出す allObjects というメソッドも定義されています。

このうちの nextObject メソッドさえオーバーライドしておけば、allObjects メソッドではそれを終了まで実行して取得できたリストを NSArray* に入れて返してくれます。

 

このようにして NSEnumerator の実装ができたら、oddEnumerator メソッドを実装します。

@implementation EzSampleList

 

// oddEnumerator メソッドでは、独自の Enumerator クラスを生成して返します。

- (NSEnumerator*)oddEnumerator

{

return [[EzSampleListOddEnumerator alloc] initWithList:_list];

}

 

NSEnumerator は一般に、使用中は元のインスタンスは編集できないことになっているので、インスタンス変数をそのまま渡しています。

 

あとはこの NSEnumerator を使用して、for ... in ループを利用することができます。

// NSEnumerator を返すプロパティであれば、for ... in 構文で使用できます。

EzSampleList* myList = [[EzSampleList alloc] init];

 

for (id item in myList.oddEnumerator)

{

}

今回の例では EzSampleListOddEnumerator の方で奇数番目の要素を取り出す処理をしていますが、EzSampleList の oddEnumerator の方で奇数番目だけのリストを作成して EzSampleListOddEnumerator ではそれを返すだけという方法でも実装できます。

[ もどる ]