对象池

作者: 烂醉花间dlitf | 来源:发表于2020-10-30 17:10 被阅读0次

UGUI 中使用的对象池

C:\Files\Unity\Projects\UGUITest\PackageSource\com.unity.ugui\Runtime\UI\Core\Utility\ObjectPool.cs 中,UGUI 实现了一个对象池,代码如下:

using System.Collections.Generic;
using UnityEngine.Events;

namespace UnityEngine.UI
{
    internal class ObjectPool<T> where T : new()
    {
        private readonly Stack<T> m_Stack = new Stack<T>();
        private readonly UnityAction<T> m_ActionOnGet;
        private readonly UnityAction<T> m_ActionOnRelease;

        public int countAll { get; private set; }
        public int countActive { get { return countAll - countInactive; } }
        public int countInactive { get { return m_Stack.Count; } }

        public ObjectPool(UnityAction<T> actionOnGet, UnityAction<T> actionOnRelease)
        {
            m_ActionOnGet = actionOnGet;
            m_ActionOnRelease = actionOnRelease;
        }

        public T Get()
        {
            T element;
            if (m_Stack.Count == 0)
            {
                element = new T();
                countAll++;
            }
            else
            {
                element = m_Stack.Pop();
            }
            if (m_ActionOnGet != null)
                m_ActionOnGet(element);
            return element;
        }

        public void Release(T element)
        {
            if (m_Stack.Count > 0 && ReferenceEquals(m_Stack.Peek(), element))
                Debug.LogError("Internal error. Trying to destroy object that is already released to pool.");
            if (m_ActionOnRelease != null)
                m_ActionOnRelease(element);
            m_Stack.Push(element);
        }
    }
}

然后我认为里面是存在一些问题的:

  • 会存在栈里面有重复元素的情况,虽然在 Release 的时候有判断是否和栈顶元素相同,但是如果出现 A 元素先被 Release 一次,然后 B 元素又被 Release,再 Release A,那么依然不能排除这种重复 Release 的情况。
  • 当一个元素被 Release 之后,依然可以被使用。所以可能需要在 Get 的时候完全的再初始化一遍,相对而言在 Release 的时候所传的方法就没有那么重要了。

下面是使用了 HashSet 进行仿写的一段代码:

using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using UnityEngine;
using UnityEngine.Events;

namespace ZhangqrTools
{
    /// <summary>
    /// 对象池
    /// T:需要有无参的构造函数
    /// </summary>
     public class ObjectPool<T> where T : new()
    {
        private HashSet<T> m_HashSet; // 使用 HashSet 是为了保证里面的元素不重复,避免多个指针指向同一个对象

        // 在调用 Get 和 Release 方法的时候对 T 元素的初始化和销毁自定义操作
        private UnityAction<T> m_OnGet;
        private UnityAction<T> m_OnRelease;

        public int AllCount
        {
            private set;
            get;
        }
        public int InactiveCount { 
            // private set { InactiveCount = value; }  // 不写就是只读的意思,类内外都不可以更改其值,如果类内部需要更改,可以将注释去掉
            get{ return m_HashSet.Count; } 
        }

        public int ActiveCount
        {
            // private set { ActiveCount = value; }
            get { return AllCount - InactiveCount; }
        }

        public ObjectPool(UnityAction<T> onGetFunction, UnityAction<T> onReleaseFunction)
        {
            m_HashSet = new HashSet<T>();
            m_OnGet = onGetFunction;
            m_OnRelease = onReleaseFunction;
        }

        public T Get()
        {
            T ret;
            if (m_HashSet.Count == 0)
            {
                ret = new T();
                AllCount++;
            }
            else
            {
                ret = m_HashSet.ElementAt(0);
                m_HashSet.Remove(ret);
            }
            m_OnGet?.Invoke(ret);
            return ret;
        }

        public void Release(T element)
        {
            if (m_HashSet.Contains(element))
            {
                Debug.LogError("The element is double pushed in object pool");
                return;
            }
            m_OnRelease?.Invoke(element);
            m_HashSet.Add(element);
        }
    }
}

然后下面是测试代码:

using System.Collections.Generic;
using UnityEngine;
using ZhangqrTools;

/// <summary>
/// 一个记录轨迹的类,维护一个数组,这个数组可能很大,所以希望在进入对象池的时候都能够清空。
/// 并且在出对象池的时候,数组第一个元素是 (0,0,0)
/// </summary>
class Track
{
    private List<Vector3> m_Points;

    public Track()
    {
        m_Points = new List<Vector3>();
    }

    public void Add(Vector3 p)
    {
        m_Points.Add(p);
    }

    public void Clear()
    {
        m_Points.Clear();
    }

    public Vector3 this[int index]
    {
        get
        {
            return m_Points[index];
        }
    }
}
class Test:MonoBehaviour
{
    private ObjectPool<Track> m_TrackPool;

    private void Awake()
    {
        m_TrackPool = new ObjectPool<Track>(TrackGet, TrackRelease);
    }

    private void TrackGet(Track track)
    {
        track.Add(Vector3.zero);
    }

    public void TrackRelease(Track track)
    {
        track.Clear();
    }

    private void Start()
    {
        Track track = m_TrackPool.Get(); 
        m_TrackPool.Release(track);    
        track = m_TrackPool.Get();    // 不加的话会报错,因为元素被重复 Release 了。
        m_TrackPool.Release(track);
    }
}

TODO:通过 HashSet 来控制集合内不会有重复元素,但是依然不能解决元素被 Release 之后依然可以在外部被使用的情况,那么就可能出现像下面的情况:

    private void Start()
    {
        Track track = m_TrackPool.Get(); 
        m_TrackPool.Release(track);    
        track = m_TrackPool.Get();    // 不加的话会报错,因为元素被重复 Release 了。
        m_TrackPool.Release(track);

        Track track2 = m_TrackPool.Get();
        m_TrackPool.Release(track);
        m_TrackPool.Release(track2); // 报错,因为元素被重复 Release 了,(track2 表示很无辜,是谁!占了我的坑)
    }

相关文章

  • Unity 类对象池资源池对象池

    类对象池 包含创建对象池,取对象池中的内容,回收。 对象管理类 因为使用加载AB包的时候可能会频繁创建类,但是ne...

  • Laya_2D示例项目使用对象池预制体的创建、位置设置和添加到父

    //使用对象池创建盒子 1.对象池的使用 对象池创建箱子 let box = Laya.Pool.getI...

  • Unity--简单的对象池

    简单的对象池分三步走: 建立对象池 拿到对象 回收对象 Test为对象池,Obj为自动回收的物体 Test.cs ...

  • 对象池

    一、对象池概述: 对于那些实例化开销比较大,并且生命周期比较短的对象,我们可以通过池就行管理。所谓池,就相当于一个...

  • 对象池

  • 对象池

    概要 看到现在,Netty之设计精妙,令人感叹。优化至极,令人发指。在高并发场景下,对象的分配的损耗是很大的,特别...

  • 对象池

    讲在前面 为什么要有对象池?我们需要一种类型的实例时候往往回去new一个这样会造成gc问题对象池根本奥义就在于一个...

  • 对象池

    UGUI 中使用的对象池 在 C:\Files\Unity\Projects\UGUITest\PackageSo...

  • 对象池

    java对象池化技术https://blog.csdn.net/tiane5hao/article/details...

  • 对象池

    using UnityEngine;using System.Collections.Generic; publi...

网友评论

      本文标题:对象池

      本文链接:https://www.haomeiwen.com/subject/ffwupktx.html