LoginSignup
2
3

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-12-19

タイトルの通りの状況です。
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のコストを減らせるでしょう

雑感

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

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3