前回はRSSフィードの一覧を読み込み、ダウンロードを行うところまで解説した。今回は、複数のフィードを並行してダウンロードするためのテクニックについて解説しよう。
NSOperationを使う
前回まででとりあえず、フィードをダウンロードできる。だが、このままではいささか使いづらい。RSSリーダでは、複数のフィードを一度にダウンロードすることが求められる。しかし、数十個のNSURLConnectionを一気に作成したら、帯域を食いつぶしてしまうだろう。同時に行うダウンロード数は、制限できるのが望ましい。1つのダウンロードが終わってから、次のダウンロードを行うようにするのだ。
Cocoaには、このような制御を行う仕組みがある。NSOperationとNSOperationQueueだ。NSOperationは、同時並行に行われる、それぞれの処理を表す。複数のNSOperationの処理を制御するのが、NSOperationQueueだ。いくつかの処理を並行して行い、1つの処理が終わったら、待機中の次の処理を行うようにする。これを使って、フィードのダウンロードを行ってみよう。
まず、ダウンロードを行うための、NSOperationのサブクラスを作る。これを、DownloadOperationという名前にしよう。DownloadOperationは、初期化時にNSURLRequestを指定するようにする。そして、ダウンロードしたデータを取り出すための、プロパティを持つ。
List 4.
@interface DownloadOperation : NSOperation
{
NSURLRequest* _request;
NSURLConnection* _connection;
NSMutableData* _data;
BOOL _isExecuting, _isFinished;
}
// プロパティ
@property (readonly) NSData* data;
// 初期化
- (id)initWithRequest:(NSURLRequest*)request;
@end
_isExecutingや_isFinishedは、処理実行のフラグになる。これらの使い方については、後述する。
このクラスでは、いくつかのNSOperationのメソッドを上書きするのだが、重要なのはstartメソッドだ。このメソッドの中で、処理を開始する。行うのはもちろん、ダウンロードだ。NSURLConnectionを作成して、ダウンロードを開始する。
List 5.
- (void)start
{
// ダウンロードを開始する
if (![self isCancelled]) {
// フラグを設定する
[self setValue:[NSNumber numberWithBool:YES] forKey:@"isExecuting"];
// コネクションを作成する
[NSURLConnection connectionWithRequest:_request delegate:self];
}
}
あとは、NSURLConnectionのデリゲートメソッドの中で、適切な処理を行ってやればいい。
これを使って、フィードをダウンロードしてみよう。まず、NSOperationQueueクラスのインスタンスを作成する。そして、DownloadOperationのインスタンスを作成し、次々とNSOperationQueueに追加してやればいい。そうすれば、適切に同時並行で処理されていくはずだ。
List 6.
// NSOperationQueueを作成する
_queue = [[NSOperationQueue alloc] init];
// フィードをダウンロードする
for (NSString* feed in feeds) {
// URLを指定する
NSURLRequest* request;
request = [NSURLRequest requestWithURL:[NSURL URLWithString:feed]];
// DownloadOperationを作成する
DownloadOperation* operation;
operation = [[DownloadOperation alloc] initWithRequest:request];
[operation autorelease];
// キー値監視を登録する
[operation addObserver:self forKeyPath:@"isFinished"
options:NSKeyValueObservingOptionNew context:nil];
// DownloadOperationを追加する
[_queue addOperation:operation];
}
キー値監視で終了通知を受け取る
これで、フィードのダウンロードが開始される。では、ダウンロードが終了した事は、どうやって受け取ればいいのだろうか? これには、キー値コーディングおよびキー値監視が使える。
キー値コーディングとキー値監視は、もともとはCocoaバインディングを実現するために導入された仕組みだ。iPhoneではCocoaバインディングは使えないが、これらの仕組みは使えるようになっている。これにより、NSNotificationとはまた別の種類の通知を行えるようになっている。
DownloadOperationをキー値コーディングに対応させるために、インスタンス変数として_isExecutingと_isFinishedを用意している。それぞれ、ダウンロード処理が実行中かどうか、完了したかどうかを表すものである。
List 5.でダウンロードを行うときに、setValue:forKey:メソッドを使ってフラグを設定した。あれが、キー値コーディングによる設定である。同様に、ダウンロードが終了したときも、isExecutingおよびisFinishedのフラグを設定することになる。このメソッドを使った値の設定を行うと、キー値監視を行っているオブジェクトに通知が飛ぶ。
通知を受け取る側は、キー値監視を行う。これには、List 6.で行っているように、addObserver:forKeyPath:options:context:を使う。通知を受け取ると、observeValueForKeyPath:ofObject:change:context:が呼び出される。確認のために、このメソッドでダウンドードしたデータの大きさを表示させてみよう。
List 7.
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object
change:(NSDictionary*)change context:(void*)context
{
// データの長さを取得する
unsigned int length;
length = [((DownloadOperation*)object).data length];
NSLog(@"data length %d", length);
// キー値監視を解除する
[object removeObserver:self forKeyPath:keyPath];
}
これで、複数のフィードをスマートにダウンロードできる。次回は、ダウンロードしたデータの処理について説明しよう。
ここまでのソースコード: RSS-1.zip