using System;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
///
/// Auto-referenced ScriptableObject instances of type T
/// An example usage for ReferenceHolder that can be used with any class type
///
public class MMReferencedScriptableObject : ScriptableObject where T : ScriptableObject
{
private MMReferenceHolder _instances;
protected virtual T Typed => _typed = _typed ?? this as T; private T _typed;
protected virtual void OnReferenced() {}
protected virtual void OnEnable()
{
_instances.Reference(Typed);
OnReferenced();
// Debug.Log(ReferenceHolder.Any != null, this);
}
protected virtual void OnDisposed() {}
protected virtual void OnDisable()
{
_instances.Dispose();
OnDisposed();
// Debug.Log(ReferenceHolder.Any != null);
}
}
// using WeakReference to let GC collect those once Engine does not use them anymore
public struct MMReferenceHolder : IDisposable where T : class
{
private static List> _instances = new List>(2);
private WeakReference _instance;
public void Reference(T instance, bool cleanUp = false)
{
_instances = _instances ?? new List>(1);
if(cleanUp) CleanUp();
if (instance != null)
{
_instance = new WeakReference(instance);
_instances.Add(_instance); // always adding at the end, to keep it cheap, do a CleanUp if needed
}
}
public void Dispose()
{
if (_instance != null) _instances?.Remove(_instance);
}
public static void CleanUp() => RepackNonNullReferences();
static void RepackNonNullReferences()
{
if (_instances == null) return;
for(int n=_instances.Count-1; n >=0; --n)
{
if (!_instances[n].TryGetTarget(out T target))
{
_instances.RemoveAt(n);
}
}
}
public static T Any => _instances != null && _instances.Count > 0 && _instances[0].TryGetTarget(out T target) ? target : null;
public static IEnumerator All
{
get
{
if (_instances == null) yield break;
foreach (var inst in _instances)
{
if (inst.TryGetTarget(out T target))
{
yield return target;
}
}
}
}
public static T First(System.Func selector)
{
if (_instances == null) return null;
if (selector == null) return Any;
foreach (var inst in _instances)
{
if (inst.TryGetTarget(out T target) && selector(target))
{
return target;
}
}
return null;
}
}
}