目次はこちら0.前回のおさらい前回、IObserverインターフェースは次のような定義であると説明しました。}}前回は解説の都合上、OnNextのみを説明しました。今回は省略してしまった「OnError」「OnCompleted」、および「Dispose」について解説します。「OnNext」「OnError」「OnCompleted」UniRxで発行されるメッセージは全てこの3種類のうちのどれかとなり、以下のような用途で利用されています。ストリームが終了したことを通知する「OnNext」メッセージOnNextはUniRxにおいて最も多用されるメッセージであり、通常「イベントの通知」(EventArgsを含む)を表しています。利用されるメッセージであり、使い方によってはこのOnNextメッセージのみを意識していれば問題ない場合も多いです。Debug.Log(x));subject.OnNext(1);subject.OnNext(2);subject.OnNext(3);subject.OnCompleted();結果123例1ではシンプルに整数を通知し、購読側でDebug.Logに表示するだけの極々単純なサンプルです。いう特殊な型を発行しています。この型は「メッセージの中身に意味は無いよ」という表明をするときに利用します。これは「イベントが発行されたタイミングが重要であって、OnNextメッセージの中身は何でもいい」という場合に利用することができます。「シーンの初期化が完了した」や「プレイヤが死亡した」といったときに利用できます。}}コルーチンでゲームの初期化を行い、処理が完了したらイベントを発行して通知するといったクラスの実装例です。このような、イベントは飛ばしたいけどイベントの中身の値は何でも良いといったシチュエーションでUnit型を使う場合が多いです。(なお、例3ではSubjectを利用しましたが、この場合ではAsyncSubjectの方が適切かもしれません。AsyncSubjectについては次回以降解説します。)「OnError」メッセージOnErrorメッセージは名前の通り例外がストリームの途中で発生した時に通知されるメッセージとなっています。OnErrorメッセージはストリームの途中でcatchして処理したり、そのままSubscribeメソッドに到達させてハンドリングさせることもできます。OnErrorメッセージがSubscribeまで到達した場合、そのストリームの購読は終了し破棄されてしまいます。format※SubscribeのオーバーロードのうちのSubscribe(OnNext,OnError)を受け取るタイプを利用しています例4では、OnNextで送られてきた文字列をSelectオペレータ(値の変換)でint型にパースして表示するストリームを使った例です。この様に、ストリームの途中で例外が発生してしまったときに、OnErrorメッセージが発行されてSubscribeに通知が来ていることがわかるかと思います。OnError受信後のOnNext("4")は処理がされていません。このように、OnErrorをSubscribeが検知するとストリームの購読を中止してしまうということを覚えておいて下さい。ストリームを再構築して購読を続行させています。OnErrorRetryはOnErrorが特定の例外であった場合に、再度Subscribeからやり直してくれる例外ハンドリング用オペレータとなっています。(ここで言うSubscribeからやり直すというのはSubjectにIObserverを再登録しにいくという意味です)例外ハンドリング用オペレータOnError発生時に例外をハンドリングすることができるオペレータがいくつか用意されています。ストリームの目指したい挙動に応じて使い分けるといいでしょう。OnErrorRetry「OnCompleted」メッセージOnCompletedは「ストリームが完了したためこれ以降メッセージを発行しない」ということを通知するメッセージです。OnCompletedメッセージがSubscribeまで到達した場合、OnError同様にそのストリームの購読は終了し破棄されます。この性質を利用し、ストリームにOnCompletedを適切に発行して上げればまとめて購読終了が実行できるため、ストリームの後始末をする場合はこのメッセージを発行するようにしましょう。一度OnCompletedを発行したSubjectは再利用不可能となります。SubscribeしてもすぐにOnCompletedが返ってくるようになります。Debug.Log("OnCompleted"));subject.OnNext(1);subject.OnNext(2);subject.OnCompleted();実行結果12OnCompleted※SubscribeのオーバーロードのうちのSubscribe(OnNext,OnCompleted)を受け取るタイプを利用しています例6のように、SubscribeにOnCompletedを受け取るオーバーロードも用意されており、これを利用することでOnCompletedを検知することができます。その1にてSubscribeには複数のオーバーロードが存在すると説明しました。実際には以下の組み合わせのオーバーロードが用意されており、利用したいメッセージに合わさえて選択するとよいでしょう。ストリームの購読終了(Dispose)つづいて、前回端折ったもうIObservableの「IDisposable」の説明をします。observer);}IDisposableはC#に用意されている既定のインターフェースであり、「リソースの開放」を行えるようにするためのメソッド「Dispose()」をただ1つ持つインターフェースです。Subscribeメソッドが返すIDisposabelのDisposeを実行すればストリームの購読を終了できるということになります。Debug.Log("OnCompleted"));subject.OnNext(1);subject.OnNext(2);//購読終了disposable.Dispose();subject.OnNext(3);subject.OnCompleted();実行結果12例7は購読をDisposeを呼び出すことで途中で中止している例です。このように、Disposeを呼び出すことで購読を任意のタイミングで停止することができます。ここで注意してほしいのは、Dispose()を実行して購読中止した場合、OnCompletedが発行されるわけではないという点です。購読中止時の処理をOnCompletedに書いていた場合、Disposeで停止させてしまうと実行されないことになってしまうので注意して下さい。Debug.Log("OnCompleted"));subject.OnNext(1);subject.OnNext(2);//ストリーム1だけ購読終了disposable1.Dispose();subject.OnNext(3);subject.OnCompleted();実行結果ストリーム1:1ストリーム2:1ストリーム1:2ストリーム2:2ストリーム2:32:OnCompleted例8は特定のSubscribeのDisposeを呼び出すことでその購読のみを中止させている例です。OnCompletedを発行すると全てのストリームが購読終了してしまいましたが、Disposeを使えば一部の購読のみを終了させることができるようになります。3.ストリームの寿命とSubscribeの終了タイミングUniRxを利用する上で特に気をつけないといけものが、ストリームのライフサイクルです。オブジェクトが頻繁に出現と削除を繰り返すUnityでは、特にここを意識しないとパフォーマンスの低下やエラーによる動作不具合を引き起こすことになってしまいます。「ストリーム」の実体は誰が持っているのか?ストリームの寿命管理を行う上で、「そのストリームは誰の持ち物なのか?」を意識する必要があります。基本的に、ストリームの実体は「Subject」であり、Subjectが破棄されればストリームも破棄されることとなります。前回も解説しましたが、「Subscribe」とはSubjectに関数を登録する処理でした。ストリームの実体はSubjectが内部に保持する「呼び出す関数リスト(及びその関数に連なるメソッドチェーン)」でであり、Subjectがストリームを管理していることになります。Subjectが破棄されればストリームも全て破棄される。逆に言えば、Subjectが残っている限りストリームは稼働し続けてしまうということになります。ストリームが参照しているオブジェクトをストリームより先に破棄して放置してしまうと、裏でストリームが動き続けたままのせいでパフォーマンスを低下させたり、メモリリークを引き起こしたり、NullReferenceExceptionを発生させてゲームを停止させてたりしてしまう可能性が出てきます。ストリームの寿命管理は細心の注意を払い、使い終わったら必ずDisposeを呼ぶ、またはOnCompletedを発行する癖をつけましょう。プレイヤの座標をイベント通知で書き換える概要アクションゲームを想定してこんな例を考えてみましょう。}}実行結果(タイマ0でプレイヤが生存していた場合)タイマ0になったときにプレイヤの座標が正しく初期位置に上書きされています。この様に、プレイヤが生存していた場合はこのコードでも正しく実行されます。実行結果(タイマ0でプレイヤが削除されていた場合)タイマ0の時点でプレイヤが削除されていた場合、このコードではMissingReferenceException例外が発生してしまいました。このコードではタイマ0の時点でプレイヤが消滅していた場合はおかしくなるということがわかりました。原因はPlayerMoverのこの部分にあります。});}先程言ったとおり、ストリームの実体はSubjectが保持しています。Vector3.zero;を呼び出してしまうということになります。Playerのtransformにアクセスしようとし、transformの取得に失敗してMissingReferenceExceptionが発生してしまう、というのがこの例外がでる原因です。この様にストリームの寿命とオブジェクトの寿命が一致していない場合、ストリームを正しく止めて上げないと動作不良を起こす原因となってしまいます。PlayerのGameObjectが破棄されたらストリームの購読を停止すればよいです。実装方法はいくつかありますが、今回は一番簡単なAddToを使った例を紹介しておきます。//指定のgameObjectが破棄されたらDisposeする}UniRxにはAddToというメソッドが用意されており、Subscribeに続けてAddTo(gameObject)と書くことで、指定のGameObjectがDestroyされた自動的にDisposeを呼び出してくれるように設定することができます。することで、Playerが削除されたら同時にストリームの購読も中止されるため、先程のような例外は発生しなくなります。upLoginユーザーは見つかりませんでした。