在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
更多内容
-
QFramework 地址: https://github.com/liangxiegame/QFramework
-
QQ 交流群: 623597263
-
Unity 进阶小班 :
-
主要训练内容:
-
框架搭建训练(第一年)
-
跟着案例学 Shader(第一年)
-
副业的孵化(第二年、第三年)
-
权益、授课形式等具体详情请查看 《小班产品手册》 :https://liangxiegame.com/master/intro
-
关注公众号:liangxiegame 获取第一时间更新通知及更多的免费内容。
290

被折叠的 条评论
为什么被折叠?



