在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

更多内容

Unity 游戏框架搭建 (十九) 简易对象池的更多相关文章

  1. Unity 游戏框架搭建 (十二) 简易AssetBundle打包工具(二)

    上篇文章中实现了基本的打包功能,在这篇我们来解决不同平台打AB包的问题. 本篇文章的核心api还是: BuildPipeline.BuildAssetBundles (outPath, 0, Edit ...

  2. Unity 游戏框架搭建 (十) QFramework v0.0.2小结

    从框架搭建系列的第一篇文章开始到现在有四个多月时间了,这段时间对自己来说有很多的收获,好多小伙伴和前辈不管是在评论区还是私下里给出的建议非常有参考性,在此先谢过各位. 说到是一篇小节,先列出框架的概要 ...

  3. Unity 游戏框架搭建 (十六) v0.0.1 架构调整

    背景: 前段时间用Xamarin.OSX开发一些工具,遇到了两个问题. QFramework的大部分的类耦合了Unity的API,这样导致不能在其他CLR平台使用QFramework. QFramew ...

  4. Unity 游戏框架搭建 2019 (九~十二) 第一章小结&第二章简介&第八个示例

    第一章小结 为了强化教程的重点,会在合适的时候进行总结与快速复习. 第二章 简介 在第一章我们做了知识库的准备,从而让我们更高效地收集示例. 在第二章,我们就用准备好的导出工具试着收集几个示例,这些示 ...

  5. Unity 游戏框架搭建 (十八) 静态扩展 + 泛型实现transform的链式编程

    本篇文章介绍如何实现如下代码的链式编程: C# this.Position(Vector3.one) .LocalScale(1.0f) .Rotation(Quaternion.identity); ...

  6. Unity 游戏框架搭建 (十五) 优雅的QChain (零)

    加班加了三个月终于喘了口气,博客很久没有更新了,这段期间框架加了很多Feature,大部分不太稳定,这些Feature中实现起来比较简单而且用的比较稳定的就是链式编程支持了. 什么是链式编程? 我想大 ...

  7. Unity 游戏框架搭建 (十四) 优雅的QSignleton(零) QuickStart

      好久不见 !之前想着让各位直接用QFramework,但是后来想想,如果正在进行的项目直接使用QFramework,这样风险太高了,要改的代码太多,所以打算陆续独立出来一些工具和模块,允许各位一个 ...

  8. Unity 游戏框架搭建 2018 (一) 架构、框架与 QFramework 简介

    约定 还记得上版本的第二十四篇的约定嘛?现在出来履行啦~ 为什么要重制? 之前写的专栏都是按照心情写的,在最初的时候笔者什么都不懂,而且文章的发布是按照很随性的一个顺序.结果就是说,大家都看完了,都还 ...

  9. Unity 游戏框架搭建 (十三) 无需继承的单例的模板

    之前的文章中介绍的Unity 游戏框架搭建 (二) 单例的模板和Unity 游戏框架搭建 (三) MonoBehaviour单例的模板有一些问题. 存在的问题: 只要继承了单例的模板就无法再继承其他的 ...

随机推荐

  1. 【Kafka】操作命令

    生产者 ./kafka-console-producer.sh --broker-list --topic norm 消费者 ./kafka-console-consumer.sh --zookeep ...

  2. 再起航,我的学习笔记之JavaScript设计模式28(委托模式)

    ## 委托模式 ### 概念介绍 **委托模式(Entrust): **多个对象接收并处理同一请求,他们将请求委托给另一个对象统一处理请求. ### 利用委托优化循环 如果我们有一个需求需要让用户点击 ...

  3. Python系列之多线程、多进程

    线程是操作系统直接支持的执行单元,因此,高级语言通常都内置多线程的支持,Python也不例外,并且,Python的线程是真正的Posix Thread,而不是模拟出来的线程. Python的标准库提供 ...

  4. 用html +js+css 实现页面轮播图效果

    html 页面 <html lang="en"> <head> <meta charset="UTF-8"> <met ...

  5. PDO浅谈之php连接mysql

    一.首先我们先说一下什么是pdo?  百科上说 PDO扩展为PHP访问数据库定义了一个轻量级的.一致性的接口,它提供了一个数据访问抽象层,这样,无论使用什么数据库,都可以通过一致的函数执行查询和获取数 ...

  6. git的使用(进阶篇)

    如何处理代码冲突 冲突合并一般是因为自己的本地做的提交和服务器上的提交有差异,并且这些差异中的文件改动,Git不能自动合并,那么就需要用户手动进行合并 如我这边执行git pull origin ma ...

  7. java基础--static关键字的使用

    一.static关键字的作用 使类成员完全独立于该类的任何对象.通常情况下,类成员必须通过它的类的对象访问,但是被static修饰的成员,能够被自己访问,而不必引用特定的实例. 一旦一个成员被声明为s ...

  8. win10 uwp 关联文件

    有时候应用需要打开后缀名为x的文件,那么如何从文件打开应用? 首先,需要打开 Package.appxmanifest 添加一个功能,需要添加最少有名称,文件类型. 上面的图就是我添加jpg 的方法, ...

  9. JavaScript函数之作用域 / 作用链域 / 预解析

    关于作用域和作用链域的问题,很多文章讲的都很详细,本文属于摘录自己觉得对自己有价值的部分,留由后用,仅供参考,需要查看详细信息请点击我给出的原文链接查看原文件 做一个有爱的搬运工~~ -------- ...

  10. JavaScript函数之实际参数对象(arguments) / callee属性 / caller属性 / 递归调用 / 获取函数名称的方法

    函数的作用域:调用对象 JavaScript中函数的主体是在局部作用域中执行的,该作用域不同于全局作用域.这个新的作用域是通过将调用对象添加到作用域链的头部而创建的(没怎么理解这句话,有理解的亲可以留 ...