为什么要对象池?
不用时时刻刻用到就去创建,不用到时销毁物体。用一个池子,将对象管理起来,分为活动的和非活动的。活动的即为在使用的,非活动的当前只在池子当中,外部不使用。
对象池的功能:能够避免对多次重复使用的对象重复实例化,造成内存碎片,GC(系统垃圾回收)过多的发生,而系统垃圾回收很消耗CPU,造成游戏顿卡,影响体验
好处:保证帧率基本稳定
坏处:会使内存略微增加
老外写的池子:Object Pooling – A Great way to Increase Performance
分析:老外写的这个池子将某一种物体的一种池子相互独立开,用一个“池子管理器”去管理所有的对象池。但是,有个地方有问题,在ObjectPool类中Shrink(…)方法将大于初始池子的对象list回归为初始大小时,存在内存泄露,只是将某一个物体的引用从list里面Remove,但是却没有将这个引用指向的物体销毁。这样会导致,这个物体的引用丢失,物体本身占据的内存却无法释放,造成内存泄露。
改进下:
//********************************************************************
// 文件名: ObjectPoolMgr.cs
// 描述: 对象池
// 作者: 李伟
// 创建时间: 2015-07-18
//
//********************************************************************
using SK;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class ObjectPoolMgr : Singleton<ObjectPoolMgr>, IMgr
{
public void Init() {}
public void Reset() {}
/*==================================================================================*/
private Dictionary<String, ObjectPool> _objectPools;
public ObjectPoolMgr()
{
_objectPools = new Dictionary<string, ObjectPool>();
}
public bool IsContainPool(string poolName)
{
return _objectPools.ContainsKey(poolName);
}
public void CreatePool(string poolName, PoolGameObject obj, int initialPoolSize, int maxPoolSize, bool canClear)
{
var pool = new ObjectPool(poolName, obj, initialPoolSize, maxPoolSize, canClear);
_objectPools.Add(poolName, pool);
}
public PoolGameObject GetObject(string poolName)
{
if (!IsContainPool(poolName)) return null;
return _objectPools[poolName].GetObject();
}
public void Destroy(string poolName, PoolGameObject obj)
{
if (!IsContainPool(poolName))
{
GameObject.DestroyObject(obj);
}
_objectPools[poolName].BackToUnUsedObjList(obj);
}
}
public class ObjectPool
{
private List<PoolGameObject> _usedObjList = new List<PoolGameObject>();
private List<PoolGameObject> _unUsedObjList = new List<PoolGameObject>();
private string _poolName;
private PoolGameObject _pooledObj;
private int _initialPoolSize;
private int _maxPoolSize;
private GameObject _poolObjsParent;
public int TotalCount { get { return totalCount(); } }
public ObjectPool(string poolName, PoolGameObject obj, int initialPoolSize, int maxPoolSize, bool canClear)
{
_poolName = poolName;
_pooledObj = obj;
_poolObjsParent = new GameObject(string.Format("{0}_{1}", _poolName, "pool"));
GameObject.DontDestroyOnLoad(_poolObjsParent);
for (int i = 0; i < initialPoolSize; i++)
{
var ObjTemp = GameObject.Instantiate(_pooledObj, Vector3.zero, Quaternion.identity) as PoolGameObject;
ObjTemp.SetObjectPool(this);
ObjTemp.transform.parent = _poolObjsParent.transform;
_unUsedObjList.Add(ObjTemp);
GameObject.DontDestroyOnLoad(ObjTemp);
}
_maxPoolSize = maxPoolSize;
_initialPoolSize = initialPoolSize;
if (canClear)
{
EventMgr.Self.ObjPoolEvents.AddClearPoolEventListener(clearPool);
}
}
public PoolGameObject GetObject()
{
for (int i = _unUsedObjList.Count - 1; i >= 0; i--)
{
var obj = _unUsedObjList[i];
if (_unUsedObjList[i] == null)
{
_unUsedObjList.Remove(obj);
continue;
}
_usedObjList.Add(obj);
_unUsedObjList.Remove(obj);
return obj;
}
if (totalCount() < _maxPoolSize)
{
var obj = GameObject.Instantiate(_pooledObj, Vector3.zero, Quaternion.identity) as PoolGameObject;
obj.SetObjectPool(this);
obj.transform.parent = _poolObjsParent.transform;
_usedObjList.Add(obj);
return obj;
}
D.Error("pool _maxPoolSize");
return null;
}
public void BackToUnUsedObjList(PoolGameObject obj)
{
if (obj == null) return;
if (totalCount() <= _maxPoolSize && _usedObjList.Contains(obj))
{
obj.transform.parent = _poolObjsParent.transform;
_unUsedObjList.Add(obj);
_usedObjList.Remove(obj);
}
else
{
GameObject.Destroy(obj);
}
}
private void clearPool()
{
int objectsToRemoveCount = totalCount() - _initialPoolSize;
if (objectsToRemoveCount <= 0)
{
return;
}
for (int i = _unUsedObjList.Count - 1; i >= 0; i--)
{
var obj = _unUsedObjList[i];
_unUsedObjList.Remove(obj);
GameObject.Destroy(obj);
}
}
public void RemoveEventListener()
{
EventMgr.Self.ObjPoolEvents.AddClearPoolEventListener(clearPool);
}
private int totalCount()
{
return _usedObjList.Count + _unUsedObjList.Count;
}
}