// In 2021.1 this is part of Unity #if !UNITY_2021_1_OR_NEWER using System; using System.Collections.Generic; namespace UnityEngine.Pool { /// /// Generic object pool implementation. /// /// Type of the object pool. internal class ObjectPool : IDisposable, IObjectPool where T : class { internal readonly Stack m_Stack; readonly Func m_CreateFunc; readonly Action m_ActionOnGet; readonly Action m_ActionOnRelease; readonly Action m_ActionOnDestroy; readonly int m_MaxSize; // Used to prevent catastrophic memory retention. internal bool m_CollectionCheck; /// /// The total number of active and inactive objects. /// public int CountAll { get; private set; } /// /// Number of objects that have been created by the pool but are currently in use and have not yet been returned. /// public int CountActive { get { return CountAll - CountInactive; } } /// /// Number of objects that are currently available in the pool. /// public int CountInactive { get { return m_Stack.Count; } } /// /// Creates a new ObjectPool. /// /// Use to create a new instance when the pool is empty. In most cases this will just be () => new T() /// Called when the instance is being taken from the pool. /// Called when the instance is being returned to the pool. This could be used to clean up or disable the instance. /// Called when the element can not be returned to the pool due to it being equal to the maxSize. /// Collection checks are performed when an instance is returned back to the pool. An exception will be thrown if the instance is already in the pool. Collection checks are only performed in the Editor. /// The default capacity the stack will be created with. /// The maximum size of the pool. When the pool reaches the max size then any further instances returned to the pool will be ignored and can be garbage collected. This can be used to prevent the pool growing to a very large size. public ObjectPool(Func createFunc, Action actionOnGet = null, Action actionOnRelease = null, Action actionOnDestroy = null, bool collectionCheck = true, int defaultCapacity = 10, int maxSize = 10000) { if (createFunc == null) throw new ArgumentNullException(nameof(createFunc)); if (maxSize <= 0) throw new ArgumentException("Max Size must be greater than 0", nameof(maxSize)); m_Stack = new Stack(defaultCapacity); m_CreateFunc = createFunc; m_MaxSize = maxSize; m_ActionOnGet = actionOnGet; m_ActionOnRelease = actionOnRelease; m_ActionOnDestroy = actionOnDestroy; m_CollectionCheck = collectionCheck; } /// /// Get an object from the pool. /// /// A new object from the pool. public T Get() { T element; if (m_Stack.Count == 0) { element = m_CreateFunc(); CountAll++; } else { element = m_Stack.Pop(); } m_ActionOnGet?.Invoke(element); return element; } /// /// Get a new which can be used to return the instance back to the pool when the PooledObject is disposed. /// /// Output new typed object. /// New PooledObject public PooledObject Get(out T v) => new PooledObject(v = Get(), this); /// /// Release an object to the pool. /// /// Object to release. public void Release(T element) { #if UNITY_EDITOR // keep heavy checks in editor if (m_CollectionCheck && m_Stack.Count > 0) { if (m_Stack.Contains(element)) throw new InvalidOperationException("Trying to release an object that has already been released to the pool."); } #endif m_ActionOnRelease?.Invoke(element); if (CountInactive < m_MaxSize) { m_Stack.Push(element); } else { m_ActionOnDestroy?.Invoke(element); } } /// /// Releases all pooled objects so they can be garbage collected. /// public void Clear() { if (m_ActionOnDestroy != null) { foreach (var item in m_Stack) { m_ActionOnDestroy(item); } } m_Stack.Clear(); CountAll = 0; } public void Dispose() { // Ensure we do a clear so the destroy action can be called. Clear(); } } } #endif