【コラム】

実践! iPhoneアプリ開発

6 RSSリーダの作り方 (2) - 複数のダウンロードを並行して処理する

    木下誠  [2009/03/13]

    前回は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

    関連したタグ

    新着記事

    特設サイトの情報

      人気記事

      一覧

        イチオシ記事

        新着記事

        特別企画

        マイナビニュースマガジン