はじめに
この記事を読んで、UniRxの Observable.FromCoroutine()
でコルーチンを起動した場合は大丈夫だったような気がしたので調べました。
Observable.FromCoroutine()
でコルーチンを起動した場合はちゃんと破棄される
Observable.Unity.cs
のソースコードを読んだら IEnumerator
を IDisposable
にキャストして Dispose()
を呼んでいました。
以下ソースコード抜粋:
..
var d = enumerator as IDisposable;
if (d != null)
{
d.Dispose();
}
..
念のため次のようなコンポーネントで実験してみましたが、コルーチンが終わる前に GameObject
を消すとちゃんと disposed
とログが出ました。
using System.Collections;
using UniRx;
using UnityEngine;
public class CoroutineTest : MonoBehaviour
{
private IEnumerator _enumerator;
void Start()
{
Observable.FromCoroutine(this.Coroutine).Subscribe().AddTo(this);
}
private IEnumerator Coroutine()
{
Debug.Log("Coroutine start");
using (Disposable.Create(() => Debug.Log("disposed")))
{
yield return new WaitForSeconds(3f);
}
Debug.Log("Coroutine end");
}
}
IEnumerator
は必ず IDisposable
を実装している?
public interface IEnumerator : IDisposable, System.Collections.IEnumerator
なので必ず IDisposable
なのですが、 IEnumerator
は
public interface IEnumerator
なので IDisposable
であるとは限りません。
その点はドキュメントによると
列挙子オブジェクトには、次の特性があります。
・IEnumerator と IEnumeratorを実装します。 T は反復子の yield 型です。
・このクラスは、System.IDisposable を実装します。
とのことで、イテレータブロックで生成された IEnumerator
は必ず IDisposable
を実装しているようです。
StartCoroutine()
を安全に使うには
StopCoroutine()
は IEnumerator
を IDisposable
にキャストして Dispose()
を呼ぶとか気の利いたことはしてくれないので、自分でやります。
using System;
using System.Collections;
using UniRx;
using UnityEngine;
public class CoroutineTest : MonoBehaviour
{
private IEnumerator _enumerator;
void Start()
{
_enumerator = this.Coroutine();
this.StartCoroutine(_enumerator);
}
void OnDestroy()
{
(_enumerator as IDisposable)?.Dispose();
this.StopCoroutine(_enumerator);
}
// コルーチンは好きなように書く
private IEnumerator Coroutine()
{
...
}
}
感想
IEnumerator
をフィールドに保存・止めるときにキャストして Dispose()
を呼ぶ手間を考えると Observable.FromCoroutine()
で起動したほうが楽そう。