Unity
UniRx
UnityDay 20

globalに置いたdelegateでメモリリークした、その対策

タイトルの通りの状況です。
singletonなりDontDestroyOnLoadを適用したオブジェクトにアタッチされたクラスなりに
デリゲートをメンバ変数として定義。
外部から関数を登録(+=)したものの解除(-=)を忘れてしまった。

http://ufcpp.net/study/csharp/MiscEventSubscribe.html
こちらでも問題が指摘されていますが
この場合、受け取り側がいつまでも解放されなくなります。
例えばCharacterManager.DispatchSignalデリゲートにEnemy.SendSignal関数を登録。
Enemy死んだのでGameObjectごと削除。
CharacterManager.DispatchSignalデリゲートから登録解除を忘れているとEnemyクラスは解放されません。
もしEnemyがmeshやtextureの参照を持っているならいつまでも解放されません。

さらに間違って無名関数を登録してしまったら解除不能になります。
メモリリークの温床ですね。

対策

デリゲートを使わずUniRXを使います
関数を登録したらIDisposableが返るので無名関数を登録しても大丈夫です

https://github.com/ufcpp/UfcppSample/blob/master/Chapters/Resource/WeakReference/WeakEvent/WeakEventExtensions.FinalizeDiposable.cs

さらに上記リンクで使われているデストラクタによるDispose忘れの検出を仕込みます
Unityなら~FinalizeDisposable()のところにDebug.LogErrorを仕込めばよいでしょう

ついでに#ifを使ってRelease時はreturn observable.Subscribe(onNext);とすれば
newのコストを減らせるでしょう

雑感

そもそもグローバルにデリゲートを置くな、というのはあります
長寿命のオブジェクトが短寿命のオブジェクトの参照を持つのはメモリリークの危険性があります
そのことを踏まえてなるべくメモリリークを自動検出できる仕組みを実装していきたいです