GameFramework解读一:对象池
1.前言【只会写出关键代码,其他代码都是可以自己扩展的,代码中对于get,set函数不会写】
嗨,大家好,我是言出必行的小白鸟。
在这个系列中我会将GameFramework中的所有模块边学习边将自己思考的东西写下来,希望可以帮助到有需要的同学~
对了,在这个系列中暂时不会使用Gameframework来做任何东西,只是将Gameframework中的模块进行解读,学习其中的思想。
【最近要实习了,虽然想做到日更,嗯。。。尽力吧!】
对象池的流程图
2.IReference
在框架中所有的对象都要继承该接口。
问:Why?
答:所有的对象都会受到对象池的管理,对象那么多,所以我们应该定一个抽象类或接口来抽象它。
问:为什么定义的是接口而不是抽象类呢?
答:因为每个对象在对象池中都是可以被复用的,所以对象应该具有一个可以重置自己的Clear方法,但是在抽象类中我们并没办法去处理,那么使用接口就很合适,当然了,使用抽象类的虚方法再覆写也没有问题。
下面展示一些 内联代码片
。
public interface IReference
{
/// <summary>
/// 清理引用。
/// </summary>
void Clear();
}
3.ReferencePool.ReferenceCollection
【在这需要注意ReferenceCollection是ReferencePool中的内部类】
问:在这个ReferencePool类中为什么会有partial关键字?
答:这是为了逻辑的清晰【一个类看上去代码少,这样别人看了才不会后害怕吧】,将一个类分成了多个部分,其中在ReferencePool中只负责对ReferenceCollection的编码,而在ReferenceCollection中才是负责对实际对象的编码。
问:这也太方便了吧!那我在ReferencePool中就只关注
ReferenceCollection,在ReferenceConllection中只关注对象就可以了!
答:是啊!
问:咦,这里还有一个关键字sealed,这是啥呀?
答:对于不需要继承的类,我们就可以使用这个sealed密封关键字来标记,你可以把它当作是一个完整的产品,不需要再进行封装了
public static partial class ReferencePool
{
//我知道,在这里我只需要关注如何去操作对象就可以了!其他什么乱七八糟的不去理他
public sealed class ReferenceCollection
{
//用来保存对象引用的队列
private readonly Queue<IReference> m_References;
//一个对象收集器负责一种类型的对象
private readonly Type m_ReferenceType;
public ReferenceCollection(Type referenceType)
{
m_References = new Queue<IReference>();
}
//让我们先来实现怎么添加引用到对象池吧!
//因为是在编写底层,那么我们就不要去使用具体的类,用泛型T来表示
public void Add<T>(int count) where T : class, IReference, new()
{
//一个对象收集器只能负责一种类型的对象,如果类型和收集器类型不匹配就报错
if (typeof(T) != m_ReferenceType)
{
throw new GameException("类型不匹配");
}
//锁住容器,虽然在Unity客户端中应该不会有多线程会访问,但在服务器的话就需要上锁以免被多个线程访问,造成异常
lock (m_References)
{
while (count-- > 0)
{
//利用Type来实例化出来对于的对象并放入队列
m_References.Enqueue((IReference)Activator.CreateInstance(m_ReferenceType));
}
}
}
#region 回收引用
public void Release(IReference reference)
{
//在前面的IReference接口中我们定义了一个Clear方法,用来重置对象,具体的重置方法由大家自己实现
reference.Clear();
//与上面一样,同样的部分不会在重复
lock (m_References)
{
if (m_References.Contains(reference))
{
throw new GameException("已经存在了该引用!");
}
m_References.Enqueue(reference);
}
}
#endregion
#region 获取引用
public T Acquire<T>() where T : class, IReference, new()
{
if (typeof(T) != m_ReferenceType)
{
throw new GameException("该泛型与指定的类型不匹配.");
}
lock (m_References)
{
if (m_References.Count > 0)
{
return (T)m_References.Dequeue();
}
}
return new T();
}
#endregion
#region 回收引用
public void Release(IReference reference)
{
//为了复用,需要清空数据
reference.Clear();
lock (m_References)
{
if (m_References.Contains(reference))
{
throw new GameException("在队列中已经存在了该对象!");
}
m_References.Enqueue(reference);
}
}
#endregion
}
}
4.ReferencePool
在源码中,有private static bool m_EnableStrictCheck这么一句,是用来判断是否需要判断变量的安全,在这就不去写这个变量了,默认就需要判断变量是否安全
public static partial class ReferencePool
{
//上面已经说了一个收集器负责一个类型,那么我们就可以用字典来表示他们的关系进行管理
private static readonly Dictionary<Type, ReferenceCollection> m_ReferenceCollections = new Dictionary<Type, ReferenceCollection>();
#region 删除引用
public static void Remove(Type referenceType,int count)
{
//对传入的Type进行安全检测
InternalCheckReferenceType(referenceType);
//利用Type来获取对于的收集器来移除对应数量的对象引用,下面的添加什么的同理
GetReferenceCollection(referenceType).Remove(count);
}
public static void RemoveAll<T>() where T : class,IReference,new()
{
GetReferenceCollection(typeof(T)).RemoveAll();
}
#endregion
#region 获取引用
public static T Acquire<T>() where T : class, IReference, new()
{
return GetReferenceCollection(typeof(T)).Acquire<T>();
}
#endregion
#region 回收引用
public static void Release(IReference reference)
{
if (reference == null)
{
throw new GameException("对象为空");
}
Type referenceType = reference.GetType();
InternalCheckReferenceType(referenceType);
GetReferenceCollection(referenceType).Release(reference);
}
#endregion
#region 添加引用
public static void Add<T>(int count) where T : class, IReference, new()
{
GetReferenceCollection(typeof(T)).Add<T>(count);
}
#endregion
private static void InternalCheckReferenceType(Type referenceType)
{
if(referenceType == null)
{
throw new Exception("报错");
}
if(!referenceType.IsClass || referenceType.IsAbstract)
{
throw new Exception("报错");
}
if (!typeof(IReference).IsAssignableFrom(referenceType)){
throw new Exception("报错");
}
}
private static ReferenceCollection GetReferenceCollection(Type referenceType)
{
if(referenceType == null)
{
throw new GameException("报错");
}
ReferenceCollection referenceCollection = null;
lock (m_ReferenceCollections)
{
if(!m_ReferenceCollections.TryGetValue(referenceType,out referenceCollection))
{
referenceCollection = new ReferenceCollection(referenceType);
m_ReferenceCollections.Add(referenceType,referenceCollection);
}
}
return referenceCollection;
}
}
至此我们就只需要创建一个继承接口的类,在测试类中调用ReferencePool中的对应的方法,就可以实现对象池的功能了