为了降低因大量产生类对象而导致的内存分配,设计了引用池的概念,来将用完的对象清理并缓存起来,供后续使用。

--E神

结构

我们直接来到 ReferencePool 定义处

可以看到GF引用池的结构非常的简洁,所有的加入了引用池的引用都存放在s_ReferenceCollections字典里,

字典的key是 Type value是ReferenceCollection。value ReferenceCollection中有一个队列用于存放继承了IReference的对象的引用。所以想要使用引用池的类都必须继承IReference

ReferenceCollection是一个绝对封闭的类只能在ReferenceCollection使用和定义

显然,引用池通过type将各种引用归类,然后存在字典里。在这个字典中每个type对应了一个系列对象引用的集合。

再看一下IReference

IReference接口只声明了一个Clear()(所有想使用引用池的类都需要)方法,因为引用池的作用是回收引用这个引用中的数据在回收之前一般来说是一定要被清理掉的。

细品

这里我们想一下为什么用队列存放一个系列对象引用的集合,为什么不用栈。事实上我认为用栈也是没什么问题的,不过队列先进先出的特性更符合我们日常的思维,你有三把一样的车往外出租,你总是希望每辆车的使用时间差不多一样,避免出现某辆车太久不用和某辆车使用过度的情况。

方法

可以看到引用池的方法和类型都是静态的,显然是会被频繁使用的方法。一般类似这种会频繁调用的部分都会做类似的处理。方便,性能友好。

获取引用

很好理解,向总池子申请引用,如果没有这个类型就添加一个新的类型,再看类型集合里面有没有引用剩余,如果有则出队一个现成引用,如果没有则实例化一个新的该类型对象返回。

清除引用,增加引用想来也很好理解就不详细说了

监视

通过此方法可以在log或者调试窗口查看引用池内部的所有引用信息的,可以方便我们发现预料之外的错误。

使用实践

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework;

public class TestNodeDate:IReference
{
    string a;

    /// <summary>
    /// 一定要有共有无参构造函数
    /// </summary>
    public TestNodeDate()
    {
       
    }

    public void Initialize(string a)
    {
        this.a = a;
    }

    public void Clear()
    {
        a = null;
    }
}

public class TestNode:MonoBehaviour
{
    public List<TestNodeDate> testNodeDates = new List<TestNodeDate>();
    
    public void Start()
    {
        for (int i = 0; i < 100; i++)
        {
            testNodeDates.Add(CreatTest(i.ToString())); //通过引用池创建对象
        }

        Debug.LogError($"------{ReferencePool.GetAllReferencePoolInfos().Length}--------");

        for (int i = 0; i < testNodeDates.Count; i++)
        {
            testNodeDates[i].Clear();
            ReferencePool.Release(testNodeDates[i]);//回收引用
        }

        testNodeDates.Clear();

        for (int i = 0; i < ReferencePool.GetAllReferencePoolInfos().Length; i++)
        {
            Debug.LogError($"---{ReferencePool.GetAllReferencePoolInfos()[i].Type.Name}----未使用引用数量-----{ReferencePool.GetAllReferencePoolInfos()[i].UnusedReferenceCount}----------");
        }

        for (int i = 0; i < 10; i++)
        {
            testNodeDates.Add(CreatTest(i.ToString())); //通过引用池创建对象
        }

        Debug.LogError("通过引用池创建对象");

        for (int i = 0; i < ReferencePool.GetAllReferencePoolInfos().Length; i++)
        {
            Debug.LogError($"---{ReferencePool.GetAllReferencePoolInfos()[i].Type.Name}---未使用引用数量------{ReferencePool.GetAllReferencePoolInfos()[i].UnusedReferenceCount}----------");
        }       
    }

    public TestNodeDate CreatTest(string a)
    {
        TestNodeDate date = ReferencePool.Acquire<TestNodeDate>();
        date.Initialize(a);
        return date;
    }
}

结果符合预期


记录历程,整理思路,共享知识,分享思维。