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

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

设计:

首先我们要弄清楚本篇对象池的几个概念,否则直接上代码大家会一头雾水。 从字面上理解对象池,池的意思就是容器。我们可以从池中获取一个对象(一条鱼),也可以向池中放入一个对象(一条鱼)。获取的操作我们叫Allocate(分配),而放入一个对象我们叫Recycle(回收)(ps:也有很多习惯叫Spawn 和 Despawn 的,这个看自己习惯了)。所以我们可以定义池的接口为如下:
    
public interface IPool < T > { T Allocate ( ) ; bool Recycle ( T obj ) ; }
为什么要用泛型呢?因为笔者开头说过,本篇主要讲如何实现一个精简并且灵活的对象池。这个灵活很大一部分是通过泛型体现的。
前面有说过,池是容器的意思,在C#中可以是List,Queue或者Stack甚至是数组。所以对象池本身要维护一个容器。本篇我们选取Stack来作为池容器,原因是当我们在Allocate和Recycle时并不关心缓存的存储的顺序,只要求缓存对象的地址是连续的。代码如下所示:
    
using System . Collections . Generic ; public abstract class Pool < T > : IPool < T > { .. . protected Stack < T > mCacheStack = new Stack < T > ( ) ; .. . }
Pool是个抽象类,为什么呢? 因为笔者开头说过,本篇主要讲如何实现一个精简并且灵活的对象池。这个灵活很大一部分是通过抽象类体现的。
现在对象的存取和缓存接口都设计好了,那么这些对象是从哪里来的呢?我们分析下,创建对象我们知道有两种方式,反射构造方法和new一个对象。对象池的一个重要功能就是缓存,要想实现缓存就要求对象可以在对象池内部进行创建。所以我们要抽象出一个对象的工厂,代码如下所示:
    
public interface IObjectFactory < T > { T Create ( ) ; }
那么大家要问为什么要用工厂? 因为笔者开头说过,本篇主要讲如何实现一个精简并且灵活的对象池。这个灵活很大一部分是通过工厂体现的。
OK,现在对象的创建,存取,缓存的接口都设计好了。下面放出Pool的全部代码。
    
using System . Collections . Generic ; public abstract class Pool < T > : IPool < T > { # region ICountObserverable /// <summary> /// Gets the current count. /// </summary> /// <value>The current count.</value> public int CurCount { get { return mCacheStack . Count ; } } # endregion protected IObjectFactory < T > mFactory ; protected Stack < T > mCacheStack = new Stack < T > ( ) ; /// <summary> /// default is 5 /// </summary> protected int mMaxCount = 5 ; public virtual T Allocate ( ) { return mCacheStack . Count == 0 ? mFactory . Create ( ) : mCacheStack . Pop ( ) ; } public abstract bool Recycle ( T obj ) ; }
代码不多,设计阶段基本就这样。下面介绍如何实现一个简易的对象池。

对象池实现

首先要实现一个对象的创建器,代码如下所示:
    
using System ; public class CustomObjectFactory < T > : IObjectFactory < T > { public CustomObjectFactory ( Func < T > factoryMethod ) { mFactoryMethod = factoryMethod ; } protected Func < T > mFactoryMethod ; public T Create ( ) { return mFactoryMethod ( ) ; } }
比较简单,只是维护了一个返回值为T的委托(如果说得有误请指正)。 对象池实现:
    
using System ; /// <summary> /// unsafe but fast /// </summary> /// <typeparam name="T"></typeparam> public class SimpleObjectPool < T > : Pool < T > { readonly Action < T > mResetMethod ; public SimpleObjectPool ( Func < T > factoryMethod , Action < T > resetMethod = null , int initCount = 0 ) { mFactory = new CustomObjectFactory < T > ( factoryMethod ) ; mResetMethod = resetMethod ; for ( int i = 0 ; i < initCount ; i ++ ) { mCacheStack . Push ( mFactory . Create ( ) ) ; } } public override bool Recycle ( T obj ) { mResetMethod . InvokeGracefully ( obj ) ; mCacheStack . Push ( obj ) ; return true ; } }
实现也很简单,这里不多说了。

如何使用?

    
var fishPool = new SimpleObjectPool < Fish > ( ( ) => new Fish ( ) , null , 100 ) ; Log . I ( "fishPool.CurCount:{0}" , fishPool . CurCount ) ; var fishOne = fishPool . Allocate ( ) ; Log . I ( "fishPool.CurCount:{0}" , fishPool . CurCount ) ; fishPool . Recycle ( fishOne ) ; Log . I ( "fishPool.CurCount:{0}" , fishPool . CurCount ) ; for ( int i = 0 ; i < 10 ; i ++ ) { fishPool . Allocate ( ) ; } Log . I ( "fishPool.CurCount:{0}" , fishPool . CurCount ) ;
运行结果:
    
fishPool . CurCount : 100 fishPool . CurCount : 99 fishPool . CurCount : 100 fishPool . CurCount : 90
OK,本篇就介绍到这里
转载请注明地址:凉鞋的笔记: liangxiegame.com

更多内容

  • QQ 交流群: 623597263
  • Unity 进阶小班 :
  • 主要训练内容:
  • 框架搭建训练(第一年)
  • 跟着案例学 Shader(第一年)
  • 副业的孵化(第二年、第三年)
  • 权益、授课形式等具体详情请查看 《小班产品手册》 :https://liangxiegame.com/master/intro
  • 关注公众号:liangxiegame 获取第一时间更新通知及更多的免费内容。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值