缓存池产生的原因:
- 1、当new一个对象后就会在内存中分配一定空间,用完后即使删除这个对象,内存中的空间也没有释放,只是断开了和这块空间的引用关系而已,所以内存占用量就会不断上升。
- 2、只有当内存占用量达到一定数量后触发垃圾回收机制才会把没用的内存释放掉,也就是触发了一次GC。触发GC需要进行大量的计算、验证之类的把那些没用的数据筛选出来,这样就会对CPU造成一定消耗,触发GC的时候很容易出现卡顿。
- 3、综上:为了解决这个问题,就需要减少new对象的次数,于是就诞生了缓存池的概念,我把一定数量的对象提前new出来放在缓存池里,这个数量一般是设置好的,在内存中的占用空间也是固定的,于是,每次需要对象的时候不再new对象了,而是去缓存池拿现有的对象。这样就避免了new对象而造成的内存空间不断减少,进而减少了触发GC的次数。
- 4、缓存适用于需要频繁创建销毁的对象。
缓存池介绍
结构
整体结构
缓存池部分
SpawnConifg的Inspector窗口编辑
接口 | 继承自接口的实体类 |
---|---|
IObjectPoo缓存池接口 | ComponentPoolOfQueue、ComponentPoolOfStack、ObjectPoolOfQueue、ObjectPoolOfStack、 |
SpwnItem实体类接口 | 这个就不写了 |
主要方法
类/接口 | 方法 | 属性 |
---|---|---|
Spawn.cs | 初始化(读取配置信息–>向缓存池添加对象)、取出对象、回收对象 | Dictionary<string, IObjectPool>类型的缓存池字典 |
IObjectPool | GetObject();取出对象、RecycleObject(T obj)回收对象 | |
PoolBuilder | 默认数量、父级Transform | |
ComponentPoolOfQueue | 三个构造方法(对象、固定数量的对象、对象数组、获取对象、回收对象 | 缓存的对象、对象栈、数量 |
SpwnItem | 这个就不写了 |
相关知识点
Srack栈:
类/接口 | 方法 |
---|---|
Push() | Push对象插入Stack的顶部(入栈操作) |
Pop() | 移除并返回Stack顶部的对象(出栈操作) |
Peek() | 返回位于Stack顶部的对象,但不移除 |
Contains() | 确定某元素是否存在Stack中 |
Clear() | 从Stack中移处所有元素 |
Count() | 获取Stack中包含的元素数量 |
ComponentPollOfStack.cs
孵化器:管理缓存池
1、管理继承于SpawnItem的对象缓存池,
2、产出继承于SpawnItem对象
3、回收继承于SpawnItem对象
/**
//孵化器,用于产出继承于SpawnItem的对象
*/
public static class Spawn{
private static Dictionary<string, IObjectPool<SpawnItem>> SpwanPools;
//孵化器初始化
[AutoLoad(1)]
private static void Initialize(){
SpawnItem.OnRecycleEvent += RecycleObject;
SpawnPools = new Dictionary<string, IObjectPool<SpawnItem>>();
SpawnConfig spawnConfig = Resources.Load<SpawnConfig>("ScriptableObject/SpawnConfig");
foreach(var item in spawnConfig.Spawns){
if(string.IsNullOrEmpty(item.Prefab.ItemName)){
Debug.LogErrorFormat("对象名不能为空!检查预制体 {0} 的ItemName属性", item.Prefab.name);
break;
}
else if(SpawnPools.ContainsKey(item.Prefab.ItemName){
Debug.LogErrorFormat("对象名冲突!无法重复创建!检查预制体 {0} 的ItemName属性", item.Prefab.name);
break;
}
else{
SpawnPools.Add(item.Prefab.ItemName, new ComponentPollOfStack<SpawnItem>(item.Count, item.Prefab));
}
}
}
//取出对象
public static T GetObject<T>(string name) where T : SpawnItem{
SpawnItem item = SpawnPools[name].GetObject();
item.IsAllowRecycle = true;
if(item is T){
return item as T;
} else {
return null;
}
}
//回收对象
private static void RecycleObject(SpawnItem symbol){
SpawnPools[symbol.ItemName].RecycleObject(symbol);
}
}
ComponentPollOfStack.cs
1、对象缓存池
2、ComponentPoolOfQueue、ObjectPoolOfQueue、ObjectPoolOfStack类似
/**
//对象缓存池
*/
public class ComponentPoolOfStack<T> : PoolBuilder, IObjectPool<T> where T :Component, new(){
//保存的对象
private T template;
//对象栈
protected Stack<T> Pool;
//栈长
public int Count {get {return Pool.Count;}}
//有参构造
public ComponentPoolOfStack(T obj){
template = obj;
Pool == new Stack<T>(MAXCOUNT);
for(int i=0; i < MAXCOUNT; i++){
T newObj = Object.Instantiate(template, Parent);
RecycleObject(newObj);
}
}
//有参构造
public ComponentPoolOfStack(int count, T obj){
template = obj;
MAXCOUNT = count;
Pool == new Stack<T>();
for(int i=0; i < MAXCOUNT; i++){
T newObj = Object.Instantiate(template, Parent);
RecycleObject(newObj);
}
}
//取出对象(出栈)
public T GetObject(){
T obj = null;
if(Pool.Count <= 0)
obj = Object.Instantiate(template, Parent);
else
obj = Pool.Pop();
obj.gameObject.SetActive(true);
return obj;
}
//回收对象(入栈)
public void RecycleObject(T obj){
if(Pool.Count >= MAXCOUNT){
Object.Destroy(obj,gameObject);
return;
}
obj.gameObject.SetActive(false);
obj.transform.SetParent(Parent);
obj.transform.localPosition = Vector3.zero;
obj.transform.rotation = Quaternion.identity;
Pool.Push(obj);
}
}
PoolBuilder.cs
声明一些缓存池需要的默认属性
public class PollBuilder{
protected int MAXCOUNT = 15;
private static Transform parent = null;
public static Transform Parent { get {return parent; } }
}
IOjectPool.cs
缓存池接口,声明了一些缓存池的方法
public interface IObjectPool<T>{
//取出对象(出栈)
T GetObject();
//回收对象(入栈)
void RecycleObject(T obj);
}
SpawnConfigEditor
管理SpawnConfig(ScriptableObject)的在窗口编辑中的数据问题
[CustomEditor(typeof(SpawnConfig))]
public class SpawnConfigEditor : Editor
{
private SpawnConfig spawn;
private void OnEnable(){
spawn = target as SpawnConfig;
}
public override void OnInspectorGUI(){
//实现Editor已经设置好的一些Inspector定义
base.OnInspectorGUI();
//获取一个按钮样式
GUIStyle style = GUI.skin.GetStyle("Button");
style.fontSize = 20;
//是否点击:按钮(按钮的文本,按钮的样式,高度(new一个高度))
//大致是读取Prefabs中的所有数据,然后将这些数据中SpawnItem类型的数据存入一个SpawnConfig类型的参数中,然后更新到ScriptableObject资源文件夹中
if (GUILayout.Button("更新Spawn列表", style, new[] { GUILayout.Height(50)})){
List<SpawnItem> items = new List<SpawnItem>();
//路径
string root = Application.dalaPath + "/Prefabs";
//是否存在此路径
if (Directory.Exists(root)) {
//获取当前目录下的所有文件路径名(包括文件夹)
string[] paths = Directory.GetFiles(root, "*", SearchOption.AllDirectories);
for (int i = 0; i < paths.length; i++) {
//切割路径名
string path = path[i].Substring(paths[i].IndexOf("Assets"));
//IO:根据(路径名、泛型)加载数据
SpawnItem item = AssetDatabase.LoadAssetAtPath<SpawnItem>(path);
if (item != null) {
items.Add(item);
}
}
List<SpawnConfig.SpawnNode> nodes = new List<SpawnConfig.SpawnNode>(item.Count);
for (int i = 0; i < items.Count; i++) {
int count = 15;
for (int j = 0; i < spawn.Spawns.Count; j++){
if(spawn.Spawns[j].Prefab.ItemName.Equals(items[i].ItemName)){
count = spawn.Spawns[j].Count;
break;
}
}
SpawnConfig.SpawnNode node = new SpawnConfig.SpawnNode();
node.Prefab = items[i];
node.Count = count;
nodes.Add(node);
}
spawn.Spawns = nodes;
}
EditorUtility.SetDirty(spawn);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
style.fontSize = 12;
}
[MenuItem("Tools/CreateSpwanConfig")]
public static void CreateAudioConfig(){
string path = "Assets/Resources/ScriptableObject/SpawnConfig.asset";
string directory = Application.dataPath + "/Resources/ScriptableObject";
if(!File.Exists(path))
{
Directory.CreateDirectory(directory);
AssetDatabase.CreateAsset(ScriptableObject.CreateInstance<SpawnConfig(), path>);
AssetDatabase.Refresh();
}
else
{
#if UNITY_EDITOR
Debug.LogWarning("SpawnConfig已存在,不允许重复创建");
#endif
}
}
}
需要了解的API
using System
using UnityEditor
Editor接口