为什么使用对象池
ok,我们先来看一个例子:假设游戏当中,玩家按下鼠标,那么游戏场景中出现一个美女A,代码是var A:美女A=new 美女A();addChild(A); 放开鼠标美女被清除,
代码是:A.dispose();A=null;如果某个玩家不停地点击鼠标,那么我们的代码将不停的NEW 美女A()而NEW 美女A()其实是很费时消耗系统性能这是问题就来了,假设NEW 美女A()消耗了2KB内存,玩家疯狂点一千次,那么我们的美女类就不断地创建,清除,创建,清除,那么我们的游戏内存直接增加1000*2kb,因为FLASH是托管的GC清理资源,具体什么时候清理只有GC知道,那么我们的游戏的性能就.........可是如果我们有了对象池那又是一种什么情况呢?首先美女将被new 美女A() 然后美女A被放入到对象池中存放,当鼠标按下的时候我们将执行:ObjectPool. borrowObject():取得美女A,当鼠标按下我们执行ObjectPool.returnObject():这样子美女又被放入到对象池中存起来了,执行一千次,由于使用对象池取得美女A和放入美女A中不涉及到对象的创建和销毁,所以我们的游戏不会导致系统资源的增加。因为美女A被访到内存池中存储起来重复利用了。
了解对象池
对象池的工作原理:
对象池的工作原理的核心有两点:使用和缓存,即对于那些被频繁使用的对象,在使用完后,不立即将它们释放,而是将它们缓存起来,以供后续的应用程序重复使用,从而减少创建对象和释放对象的次数,进而改善应用程序的性能。
优点:能快速取出对象节省了NEW对象所产生的cpu,时间的消耗。能很好的控制内存的占用,使用时从对象池取出,使用完毕放回。中间不涉及到对象销毁创建,所以内存占用是定量的。同时如果对象池提前
缺点:对象池同样消耗new同样对象所消耗的时间,对象池从创建到结束消耗定量的内存。对象池只适合大量的对象需要被重复创建使用销毁创建使用销毁的情况。非提前初始化式内存池,缓存as值类型的对象如:Point反而产生更大的消耗。
要实现一个对象池,一般会涉及到以下的二个类:
Reusable.as //池中对象模型
ReusablePool.as //对象池写入读取存取类
具体源码:
Reusable.as
package app.utility.pool {
/**
* 池属性
* 对象属性
* @author silva
*/
public class Reusable {
/**
* DisplayObject 对象 子SWF
*/
public var object:Object;
/**
* 对象池唯一给予对象的标识符
*/
public var name:String;
/**
* 新建一个Reusable对象
* @param _obj 放入的对象
* @param _name 对象的标识
*/
public function Reusable(_obj:Object, _name:String):void {
if (_name == "") {
throw new Error("对象标识不能为空!");
return;
}
object = _obj;
name = _name;
}
}
}
---------------------》
ReusablePool.as
package app.utility.pool {
import app.utility.debug.Debug;
/**
* 对象池
* @example
* 使用示例
* <listing version='3'>
* private var reusablepool:ReusablePool = ReusablePool.getInstance();
* if (!reusablepool.hasReusable("swfUI")) {
* reusablepool.setReusable(new Reusable(loadmode.LoaderObj, "swfUI"));
* }
* var myReusable:Reusable = reusablepool.getReusable("swfUI");
* var myLoader:Loader = myReusable.object as Loader;
* addChild(myLoader);
* </listing>
* @author silva
*/
public class ReusablePool {
/**
*
*/
private static var reusablePool : ReusablePool;
/**
* 池对象
*/
private static var reusable:Reusable;
/**
* 用于存取对象
*/
private static var reusableArr:Array = new Array();
/**
* 最大对象数量
*/
private static var maxPoolSize:int = 1000;
/**
* @private
* 此类为单例 不要实例化
* @param enforcer
*/
public function ReusablePool(enforcer:AccessRestriction):void {
if ( enforcer == null )
{
throw new Error("此类为单例 不要实例!" );
}
}
/**
* 单例此类
* @return ReusablePool
*/
public static function getInstance() : ReusablePool {
if( reusablePool == null ) {
reusablePool = new ReusablePool(new AccessRestriction());
}
return reusablePool;
}
/**
* 设置最大对象数量
* @param value
*/
public function setMaxPoolSize(value:Number):void {
maxPoolSize = value;
}
/**
* 获取池中对象
* @param name 获取对象的标识字符
* @return Reusable对象
*/
public function getReusable(name:String):Reusable {
var index:int = -1;
var len:int = reusableArr.length;
for (var i:int = 0; i < len; i++) {
if (reusableArr[i].name == name) {
index = i;
break;
}
}
if (index == -1) {
throw new Error("查询不到此对象-> " + name);
}
reusable = new Reusable(reusableArr[index].object, reusableArr[index].name);
return reusable;
}
/**
* 存储对象到池中
* @param reusable
*/
public function setReusable(reusable:Reusable):void {
if (hasReusable(reusable.name)) {
Debug.warning("#重复存储相同对象或对象标识重复!");
return;
}
if (reusableArr.length < maxPoolSize) {
reusableArr.push({object: reusable.object, name: reusable.name});
}else {
throw new Error("对象池已满,请清除不需要的对象或设置对象池setMaxPoolSize");
}
}
/**
* 删除池中指定标识的对象
* @param name 要删除对象的标识符
*/
public function removeReusable(name:String):void {
var index:int = -1;
var len:int = reusableArr.length;
for (var i:int = 0; i < len; i++) {
if (reusableArr[i].name == name) {
index = i;
break;
}
}
if (index == -1) {
throw new Error("查询不到要删除的对象-> " + name);
return;
}
reusableArr.splice(i, 1);
}
/**
* 检查对象是否在池中
* @param name 对象标识
* @return Boolean
*/
public function hasReusable(name:String):Boolean {
var len:int = reusableArr.length;
for (var i:int = 0; i < len; i++) {
if (reusableArr[i].name == name) {
return true;
break;
}
}
return false;
}
}
}
class AccessRestriction {}
-----------------------------------------------------------
用法例子:
private var reusablepool:ReusablePool = ReusablePool.getInstance();
private var CITYSTAM:String="citystam";
private function loads():void{
//检查是否对象池记录了CITYSTAM的标识?
if (!reusablepool.hasReusable(CITYSTAM)) {
//没有记录 就Loader一次swf文件
var loadmode:Loader = new Loader();
loadmode.load(new URLRequest("CityStaM.swf"));
loadmode.addEventListener(Event.COMPLETE, loadStaComplete);
}else {
//记录了的话 就直接执行loadStaComplete方法
loadStaComplete();
}
}
private function loadStaComplete(e:Event = null):void {
//是否在对象池中
if (!reusablepool.hasReusable(CITYSTAM)) {
//没有在就写入对象池
reusablepool.setReusable(new Reusable(loadmode.loader, CITYSTAM));
}
//读取对象池中标识为CITYSTAM的数据
var myReusable:Reusable = reusablepool.getReusable(CITYSTAM);
//读取Loader对象
var myLoader:Loader = myReusable.object as Loader;
}
以上代码 写入和读取存取于一体
第一次执行loads方法是什么会将swf的loader对象写入对象池 第二执行的时候就不需要写入了 因为之前写过了 so 只需要读取 这样就有效的控制了带宽和内存的增加