Objective-Cをちゃんと勉強して来なかったので、ここらでちゃんと勉強しようかと思いたった。まずは手習いとして、結城浩氏の『Java言語で学ぶデザインパターン入門』の各パターンでも移植してみよう。コード自体の意味や内容についてはそちらを参照してほしい。
第一回はIterator
パターン。
Objective-CではFoundationにIterator
を実装したNSEnumerator
が提供されているが、敢て実装してみる。
Objective-C自身は、オブジェクト指向の部分はC++などと違ってSmalltalkに近く、純粋に「らしい」コードを追求すればもっとよいコードがあるだろう。まず、JavaでいうInterfaceのようなものはないし、動的な指向が強く全てのオブジェクトがNSObject
というクラスに辿れるので本当はもう少し柔軟なコードが簡単に書けそうな気がする。が、ここは手習いなのでJavaのコードに準じたコードにしてみた。
まず、Javaのインターフェイスとして定義されているIterator
とAggregate
はObjective-Cでは少し異端とも思える静的な型付けを意識したProtocol
を用いて以下のようなコードとする。
Iterator.h
@protocol Iterator
-(BOOL)hasNext;
-(id)next;
@end
Aggregate.h
@protocol Aggregate
-(id <Iterator>)iterator;
@end
これら抽象クラスの実体としてBookShelfIterator
とBookShelf
を用意する。それぞれの宣言は以下の通り
BookShelfIterator.h
@interface BookShelfIterator : NSObject <Iterator> {
BookShelf* bookShelf;
int index;
}
-(id)initWithBookShelf:(BookShelf*)shelf;
@end
Aggregate
の中での集合の扱いはNSMutableArray
を使用する。
BookShelf.h
@interface BookShelf : NSObject <Aggregate>
{
NSMutableArray* books;
int last;
}
-(Book*) getBookAtIndex:(int) index;
-(void) appendBook:(Book*)book;
-(int)getLength;
-(id <Iterator>) iterator;
@end
で、BookShelfIterator
のIterator
としてインターフェイス部分の実装は以下のような感じ
-(BOOL)hasNext
{
if(index < [bookShelf getLength]) {
return TRUE;
} else {
return FALSE;
}
}
-(id)next
{
Book* book = [bookShelf getBookAtIndex:index];
index++;
return book;
}
@end
Javaの例に即したテストコードは以下のようになる。 Book* book;
book = [[[Book alloc] initWithString:@"Around the World in 80 Days"] autorelease];
[bookShelf appendBook: book];
book = [[[Book alloc] initWithString:@"Bible"] autorelease];
[bookShelf appendBook: book];
book = [[[Book alloc] initWithString:@"Cinderella"] autorelease];
[bookShelf appendBook: book];
book = [[[Book alloc] initWithString:@"Daddy-Long-Legs"] autorelease];
[bookShelf appendBook: book];
id <Iterator> it = [bookShelf iterator];
while([it hasNext]) {
Book* book = [it next];
NSLog(@"%@",[book getName]);
}
[bookShelf release];
恐らくProtocolを使う必要性は低いのだろうが……
デザインパターンの部分は余り考えることもなく終ったが、Javaのプログラマーが嵌りそうなところは、メモリー管理関連。いまではガベージコレクションもサポートされているが、デフォルトではプロジェクトの設定でオフになっている。自身でやるときは、自身でのretain/release
とautorelease
を上手く意識して使うことが必要。
また、Cocoa/Foundationのクラスでは、[[Hogehoge alloc] init]
で初期化すると普通にretain/release
が必要だが、[Hogehoge hogehoge]
や [Hogehoge hogehogeWithString]
などのネーミングの初期化はautorelease
が掛っている。なので、これらのオブジェクトはそのように扱わなければならない。また、GUIでなくコマンドラインのプログラムの場合、NSAutoreleasePool
が用意されない。Cocoa/Foundationのライブラリでは先の例のようにNSAutoreleasePoolを前提にしているものがあるので、自身で以下のように用意しなけばならない。
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// プログラムの処理
[pool drain];
最初これに気付かずに悩んでしまった。
もっとも、XCodeで新規プロジェクトを作成する際、「Command Line Utlity」
のなかから「Foundation Tool」を選んで作成すれば、このあたりのコードは
埋まったテンプレートを用意してくれるのだが。
次はAdapter
かな。
ソフトバンククリエイティブ
売り上げランキング: 5728
コメント
コメントを投稿