Unity 游戏框架搭建 (十九) 简易对象池

在Unity中我们经常会用到对象池,使用对象池无非就是解决两个问题:

  • 一是减少new时候寻址造成的消耗,该消耗的原因是内存碎片。
  • 二是减少Object.Instantiate时内部进行序列化和反序列化而造成的CPU消耗。想进一步了解对象池模式优化原理的同学可以参阅: 对象池模式:http://gpp.tkchu.me/object-pool.html,本篇主要讲如何实现一个精简并且灵活的对象池。

设计:

首先我们要弄清楚本篇对象池的几个概念,否则直接上代码大家会一头雾水。从字面上理解对象池,池的意思就是容器。我们可以从池中获取一个对象(一条鱼),也可以向池中放入一个对象(一条鱼)。获取的操作我们叫Allocate(分配),而放入一个对象我们叫Recycle(回收)(ps:也有很多习惯叫Spawn和Despawn的,这个看自己习惯了)。所以我们可以定义池的接口为如下:

  1. public interface IPool<T>
  2. {
  3. T Allocate();
  4.  
  5. bool Recycle(T obj);
  6. }

为什么要用泛型呢?因为笔者开头说过,本篇主要讲如何实现一个精简并且灵活的对象池。这个灵活很大一部分是通过泛型体现的。

前面有说过,池是容器的意思,在C#中可以是List,Queue或者Stack甚至是数组。所以对象池本身要维护一个容器。本篇我们选取Stack来作为池容器,原因是当我们在Allocate和Recycle时并不关心缓存的存储的顺序,只要求缓存对象的地址是连续的。代码如下所示:

  1. using System.Collections.Generic;
  2.  
  3. public abstract class Pool<T> : IPool<T>
  4. {
  5. ...
  6. protected Stack<T> mCacheStack = new Stack<T>();
  7. ...
  8. }

Pool是个抽象类,为什么呢? 因为笔者开头说过,本篇主要讲如何实现一个精简并且灵活的对象池。这个灵活很大一部分是通过抽象类体现的。

现在对象的存取和缓存接口都设计好了,那么这些对象是从哪里来的呢?我们分析下,创建对象我们知道有两种方式,反射构造方法和new一个对象。对象池的一个重要功能就是缓存,要想实现缓存就要求对象可以在对象池内部进行创建。所以我们要抽象出一个对象的工厂,代码如下所示:

  1. public interface IObjectFactory<T>
  2. {
  3. T Create();
  4. }

那么大家要问为什么要用工厂? 因为笔者开头说过,本篇主要讲如何实现一个精简并且灵活的对象池。这个灵活很大一部分是通过工厂体现的。

OK,现在对象的创建,存取,缓存的接口都设计好了。下面放出Pool的全部代码。

  1. using System.Collections.Generic;
  2.  
  3. public abstract class Pool<T> : IPool<T>
  4. {
  5. #region ICountObserverable
  6. /// <summary>
  7. /// Gets the current count.
  8. /// </summary>
  9. /// <value>The current count.</value>
  10. public int CurCount
  11. {
  12. get { return mCacheStack.Count; }
  13. }
  14. #endregion
  15.  
  16. protected IObjectFactory<T> mFactory;
  17.  
  18. protected Stack<T> mCacheStack = new Stack<T>();
  19.  
  20. /// <summary>
  21. /// default is 5
  22. /// </summary>
  23. protected int mMaxCount = 5;
  24.  
  25. public virtual T Allocate()
  26. {
  27. return mCacheStack.Count == 0
  28. ? mFactory.Create()
  29. : mCacheStack.Pop();
  30. }
  31.  
  32. public abstract bool Recycle(T obj);
  33. }

代码不多,设计阶段基本就这样。下面介绍如何实现一个简易的对象池。

对象池实现

首先要实现一个对象的创建器,代码如下所示:

  1. using System;
  2.  
  3. public class CustomObjectFactory<T> : IObjectFactory<T>
  4. {
  5. public CustomObjectFactory(Func<T> factoryMethod)
  6. {
  7. mFactoryMethod = factoryMethod;
  8. }
  9.  
  10. protected Func<T> mFactoryMethod;
  11.  
  12. public T Create()
  13. {
  14. return mFactoryMethod();
  15. }
  16. }

比较简单,只是维护了一个返回值为T的委托(如果说得有误请指正)。对象池实现:

  1. using System;
  2.  
  3. /// <summary>
  4. /// unsafe but fast
  5. /// </summary>
  6. /// <typeparam name="T"></typeparam>
  7. public class SimpleObjectPool<T> : Pool<T>
  8. {
  9. readonly Action<T> mResetMethod;
  10.  
  11. public SimpleObjectPool(Func<T> factoryMethod, Action<T> resetMethod = null,int initCount = 0)
  12. {
  13. mFactory = new CustomObjectFactory<T>(factoryMethod);
  14. mResetMethod = resetMethod;
  15.  
  16. for (int i = 0; i < initCount; i++)
  17. {
  18. mCacheStack.Push(mFactory.Create());
  19. }
  20. }
  21.  
  22. public override bool Recycle(T obj)
  23. {
  24. mResetMethod.InvokeGracefully(obj);
  25. mCacheStack.Push(obj);
  26. return true;
  27. }
  28. }

实现也很简单,这里不多说了。

如何使用?

  1. var fishPool = new SimpleObjectPool<Fish>(() => new Fish(), null, 100);
  2.  
  3. Log.I("fishPool.CurCount:{0}", fishPool.CurCount);
  4.  
  5. var fishOne = fishPool.Allocate();
  6.  
  7. Log.I("fishPool.CurCount:{0}", fishPool.CurCount);
  8.  
  9. fishPool.Recycle(fishOne);
  10.  
  11. Log.I("fishPool.CurCount:{0}", fishPool.CurCount);
  12.  
  13. for (int i = 0; i < 10; i++)
  14. {
  15. fishPool.Allocate();
  16. }
  17.  
  18. Log.I("fishPool.CurCount:{0}", fishPool.CurCount);

运行结果:

  1. fishPool.CurCount:100
  2. fishPool.CurCount:99
  3. fishPool.CurCount:100
  4. fishPool.CurCount:90

OK,本篇就介绍到这里

相关链接:

我的框架地址:https://github.com/liangxiegame/QFramework

教程源码:https://github.com/liangxiegame/QFramework/tree/master/Assets/HowToWriteUnityGameFramework/

QFramework &游戏框架搭建QQ交流群: 623597263

转载请注明地址:凉鞋的笔记http://liangxiegame.com/

微信公众号:liangxiegame

19.简易对象池  - 图1

如果有帮助到您:

如果觉得本篇教程对您有帮助,不妨通过以下方式赞助笔者一下,鼓励笔者继续写出更多高质量的教程,也让更多的力量加入 QFramework 。