2013/12/19

ReactiveCocoa 3.0のconcat:とcatch:について

RACSequenceRACStreamは3.0でDeprecateになる予定になっており、 コレクション周りの処理を行いたければ、Underscore.mRXCollectionsなどのサードパーティライブラリを使ってね、ということになりました (ちなみに、現時点でのReactiveCocoaの安定版は2.0なので、3.0は開発版です)。

これによって、RACSequenceRACStreamにあったメソッドがRACSignalに移動して、コードも非常にわかりやすくなりました。

今回はその例として、concat:catch:のコードを見てみます。

concat:catchTo:の動作について

簡単に言うと、concat:catchTo:はどちらも2つのシグナルをくっつけてひとつのシグナルに見せかけます。

そのくっつける条件が異なっていて、レシーバであるシグナルオブジェクトが、CompletedとErrorのどちらのイベントで終わるかで決まります。

  • concat:はレシーバがCompletedイベントで終わるなら、くっつける
  • catchTo:はレシーバがErrorイベントで終わるなら、くっつける

具体的な例で見てみましょう。例えば、次の3つのシグナルがあったとします。

  • シグナルACはnextイベントxとnextイベントyを送信したあとに、Completedイベントで終わる (これをAC ::= xy → Completedと表すことにします)
  • シグナルAE ::= xy → Error
  • シグナルB ::= z → Completed

このとき、これらをconcat:catchTo:で結合したシグナルは、それぞれ次のような結果が得られます。

  • [AC concat:B] ::= xyz → Completed
  • [AE concat:B] ::= xy → Error
  • [AC catchTo:B] ::= xy → Completed
  • [AE catchTo:B] ::= xyz → Completed

コード

ではコードを見ていきましょう。なお、以下のコードでは説明に不要な個所はオリジナルコードから省いています。

concat:

最初5行くらいは無視して、next:あたりからのコードに注目してください。

- (RACSignal *)concat:(RACSignal *)signal {
    return [RACSignal create:^(id<RACSubscriber> subscriber) {
        [self subscribeSavingDisposable:^(RACDisposable *disposable) {
            [subscriber.disposable addDisposable:disposable];
        } next:^(id x) {
            [subscriber sendNext:x];
        } error:^(NSError *error) {
            [subscriber sendError:error];
        } completed:^{
            [signal subscribe:subscriber];
        }];
    }];
}

nextイベントとerrorイベントはただ転送しているだけですが、completedイベントについては、引数のシグナルに繋げていることがわかります。

catch:

このメソッドは上のconcat:と似ていることに気付いたでしょうか? 違う点は引数の型がblockになっていることと、 errorイベントとcompletedイベントあたりの処理がconcat:とは逆になっていることです。

- (RACSignal *)catch:(RACSignal * (^)(NSError *error))catchBlock {
    return [RACSignal create:^(id<RACSubscriber> subscriber) {
        [self subscribeSavingDisposable:^(RACDisposable *disposable) {
            [subscriber.disposable addDisposable:disposable];
        } next:^(id x) {
            [subscriber sendNext:x];
        } error:^(NSError *error) {
            RACSignal *signal = catchBlock(error);
            [signal subscribe:subscriber];
        } completed:^{
            [subscriber sendCompleted];
        }];
    }];
}

catch:では、nextイベントとcompletedイベントはただ転送しているだけですが、errorイベントについては、引数のblockが生成するシグナルに繋げていることがわかります。

最初の説明に出てきていたcatchTo:は、このcatch:を使って実現されています。

catchTo:

このコードはReactiveCocoa 2.0と同じです。

- (RACSignal *)catchTo:(RACSignal *)signal {
    return [self catch:^(NSError *error) {
        return signal;
    }];
}

おわりに

ReactiveCocoa 3.0のconcat:catch:のコードを簡単に紹介しました。

関連リンク

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。