Zenject
Zenject copied to clipboard
IDisposable installed AsTransient is not disposed
Describe the bug When having an AsTransient binding for an IDisposable class in an installer it automatically creates an instance (like non-lazy initialization). But this instance is not entered into the DisposableManager and therfore gets not disposed when all dynamically created instances of the class are disposed.
To Reproduce Have a class inheriting from IDisposable. Add it to an installer with AsTransient. This already creates an instance once the installer is executed, but the instance will never call its Dispose().
Expected behavior Either IDisposable classes when installed AsTransient are not automatically creating an instance. Or otherwise IDisposable classes instances created in that way should be added to the DisposableManager automatically, so they are disposed correctly.
Extenject and Unity info (please complete the following information):
- Zenject version: 9.2.0
- Unity version: 2018.4.3 annd 2020.3.6
- Project's scripting backend [e.g. Mono/IL2CPP]: Mono
Experiencing the same issue. I also found that even though I have 2 places I use a transient dependency it gets created 3 times. Then on scene change only the third (redundant one) gets disposed.
After some debugging I think I found a root cause: when IDisposable is used together with AsTransient() a new instance will be created and passed to DisposableManager. It simply won't even try to get existing instances issued by the TransientProvider - this is what AsTransient does really and there is no special treatment for interfaces like IDisposable IInitializable and ITickable.
This would need some rework on how transpent dependencies handling works.
In the meantime as a temporary workaround I would suggest creating a factory that would hold references to all transient instances created (registered AsSingle with IDisposable/tickable/initializable) and then it would call respective methods on the actual transient dependencies.
Here is an example workaround:
public sealed class DisposableTracker<T> : IDisposable where T : IDisposable
{
private readonly List<T> objects = new List<T>();
[Preserve]
public DisposableTracker() { }
public void Dispose()
{
this.objects.ForEach(m => m.Dispose());
this.objects.Clear();
}
public void Track(T instance)
{
this.objects.Add(instance);
}
}
And then in Install bindings method in your installer
var disposableTracker = new DisposableTracker<Foo>();
this.Container.BindInterfacesTo<DisposableTracker<Foo>>()
.FromInstance(disposableTracker);
this.Container.Bind<Foo>()
.AsTransient()
.OnInstantiated<Foo>((ctx, instance) => disposableTracker.Track(instance));