【Unity】ZenjectのAsSingle(),AsCached(),AsTransient()の違い
Zenject使うじゃん
— ペンギン (@YutaDevelop) 2017年4月21日
AsSingleでBindするじゃん
UnBindするじゃん
もう一回Bindするじゃん
Injectされない!!!!!!
AsTransient使えば解決なんだけど、GC走って回収されてるのにキャッシュ残ってるってどういうことなんなんなん
調べてみた。
超適当にテストコード書いて挙動を確かめた
メソッドの名前が意味不明なのは許してほしい
挙動を理解しないとヤバい事故生みそうだなこれという結果になった
結果的にはAsSingle()でInjectされてる参照がUnbindで破棄されてなくて、Inject済みの参照をもう一回Bindし直してた
あとGCで回収されてなかった
そもそも
As〜ってなんぞやというと
BindするクラスやインスタンスのBind方法の定義である
https://github.com/modesttree/Zenject#binding
AsSingle(),AsCached()はキャッシュするのね〜
AsTransientはキャッシュ作らないのね〜くらいの認識だと
しぬ(しんだ)
AsTransient()
クラスをBindする際に複数インスタンスの生成を許容する
クラスがContainerにBindされている際、参照情報が一つであればResolveで解決出来る
しかし、Resolveは単一のインスタンスに対してのみ参照を解決するため
AsTransientでBindを複数回行っていた場合、multiple instanceエラーを吐く
AsTransientでのBindには必ず.WithId()を使用してIdentiferを定義するか
ResolveAllを使用した参照解決を図るようにした方が良い
また、As〜などの定義をしなかった場合はデフォルトでAsTransientになる
Factoryなどを使用したインスタンス生成もAsTransientになるため、Instantiateで複数生成したクラスに対してResolveするとエラー吐く、気をつけよう
(まあInstantiateした時点でResolveすることなんてほとんどないんだけど)
AsCached()
Bindされているクラスがすでにインスタンス化されている場合、同じインスタンスを使い回すようにする
また、UnbindするとCacheされていたインスタンスは全て参照を破棄される
同じインスタンスは使いまわしたいが、Unbindで破棄したい時はこれを使用する
クセがなく、取り回しがしやすいので動的Bindをする際は非常におすすめしたい
AsSingle()
クラスをBindする際にSingletonとして定義する
クラスがContainerにBindされている際は
「そのContainer内で唯一存在するクラス」となることが保障される
Bindされている際は、Cachedと同じ挙動をする
しかし、Unbindを呼んだ際に
Bindした自身の参照を他のクラスにInjectしなくなるが、生成したインスタンス自体は破棄されずキャッシュされたまま
のため、Unbindを呼び、その後もう一度同じクラスをBindしても
new()などが走らず、キャッシュされていた参照をもう一度Bindして使い回す
(HashCodeでやっているが、他にもResolveで返ってきた値の中身を書き換えたところどちらも書き換わった)
AsSingle()は必ず一つ、かつ一度しか生成されないオブジェクトに対してのみ使用しよう
まとめ
readmeには、「AsSingle()をほとんどの場面で使いたいと思うでしょうが〜」とか書いてあるけど
基本AsCached()でいいわ。
AsSingle()で動的Bindすると初期化走らせられなくてビビった結果、動作を知れたのでまあよかったと思う。