Flash 平台技术的优化(二) 节省内存

来源:互联网 发布:淘宝商城羊毛衫 编辑:程序博客网 时间:2024/05/21 00:48

      节省内存

      对于应用程序(尤其桌面应用程序)开发一直非常重要。然而,内存的使用量对移动设备来说非常重要,因此有必要限制应用程序占用的内存量。显示对象选择适当的显示对象。
ActionScript 3.0 包含很多显示对象。要限制内存用量,最简单的优化技巧之一是使用合适类型的显示对象。对于非交互式简单形状,请使用 Shape 对象。对于不需要时间轴的交互式对象,请使用 Sprite 对象。对于使用时间轴的动画,请使用MovieClip 对象。应始终为应用程序选择最有效的对象类型。
以下代码显示不同显示对象的内存使用量:
trace(getSize(new Shape()));
// output: 236
trace(getSize(new Sprite()));
// output: 412
trace(getSize(new MovieClip()));
// output: 440
getSize() 方法显示对象在内存中占用的字节数。可以看到,如果不需要 MovieClip 对象的功能,使用多个 MovieClip 对象(而不是使用简单的 Shape 对象)会浪费内存。
      原始类型
      使用 getSize() 方法为代码设置基准并确定任务的最有效对象。所有原始类型(String 除外)在内存中都是占用 4 – 8 个字节。使用特定类型的基元无法优化内存:

// Primitive types
var a:Number;
trace(getSize(a));
// output: 8
var b:int;
trace(getSize(b));
// output: 4
var c:uint;
trace(getSize(c));
// output: 4
var d:Boolean;
trace(getSize(d));
// output: 4
var e:String;
trace(getSize(e));
// output: 4
如果没有为表示 64 位值的数字分配值, ActionScript 虚拟机 (AVM) 将为其分配 8 个字节。所有其他原始类型存储时均占用
4 个字节。
// Primitive types
var a:Number = 8;
trace(getSize(a));
// output: 4
a = Number.MAX_VALUE;
trace(getSize(a));
// output: 8
此行为不同于 String 类型。分配的存储量基于 String 的长度:
var name:String;
trace(getSize(name));
// output: 4
name = "";
trace(getSize(name));
// output: 24
name = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and
scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into
electronic typesetting, remaining essentially unchanged. It was popularized in the 1960s with the release
of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like
Aldus PageMaker including versions of Lorem Ipsum.";
trace(getSize(name));
// output: 1172
使用 getSize() 方法为代码设置基准确定任务的最有效对象。
     重用对象
     尽可能重复使用对象而不是重新创建对象。优化内存的另一种简单方法是尽可能重复使用对象并避免重新创建对象。例如,在循环中,不要使用以下代码:

const MAX_NUM:int = 18;
const COLOR:uint = 0xCCCCCC;
var area:Rectangle;
for (var:int = 0; i < MAX_NUM; i++)
{
// Do not use the following code
area = new Rectangle(i,0,1,10);
myBitmapData.fillRect(area,COLOR);
}
在各个循环迭代中重新创建 Rectangle 对象将使用更多内存且速度更慢,因为将在各个迭代中创建一个新对象。请使用以下方
法:
const MAX_NUM:int = 18;
const COLOR:uint = 0xCCCCCC;
// Create the rectangle outside the loop
var area:Rectangle = new Rectangle(0,0,1,10);
for (var:int = 0; i < MAX_NUM; i++)
{
area.x = i;
myBitmapData.fillRect(area,COLOR);
}
上面的示例中使用的对象对内存影响相对较小。下面的示例说明通过重复使用 BitmapData 对象可以节省更多的内存。以下用
于创建平铺效果的代码浪费了内存:
var myImage:BitmapData;
var myContainer:Bitmap;
const MAX_NUM:int = 300;
for (var i:int = 0; i< MAX_NUM; i++)
{
// Create a 20 x 20 pixel bitmap, non-transparent
myImage = new BitmapData(20,20,false,0xF0D062);
// Create a container for each BitmapData instance
myContainer = new Bitmap(myImage);
// Add it to the display list
addChild(myContainer);
// Place each container
myContainer.x = (myContainer.width + 8) * Math.round(i % 20);
myContainer.y = (myContainer.height + 8) * int(i / 20);
}
注: 使用正值时,将舍入值转换为整数比使用 Math.floor() 方法快得多。
以下图像显示位图平铺效果:


优化的版本创建了由多个 Bitmap 实例引用的单一 BitmapData 实例,并产生相同的效果:
// Create a single 20 x 20 pixel bitmap, non-transparent
var myImage:BitmapData = new BitmapData(20,20,false,0xF0D062);
var myContainer:Bitmap;
const MAX_NUM:int = 300;
for (var i:int = 0; i< MAX_NUM; i++)
{
// Create a container referencing the BitmapData instance
myContainer = new Bitmap(myImage);
// Add it to the display list
addChild(myContainer);
// Place each container
myContainer.x = (myContainer.width + 8) * Math.round(i % 20);
myContainer.y = (myContainer.height + 8) * int(i / 20);
}
此方法节省了大约 700 KB 的内存,这对于传统移动设备相当可观。使用 Bitmap 属性,不必更改原始 BitmapData 实例即可
操纵每个位图容器:


// Create a single 20 x 20 pixel bitmap, non-transparent
var myImage:BitmapData = new BitmapData(20,20,false,0xF0D062);
var myContainer:Bitmap;
const MAX_NUM:int = 300;
for (var i:int = 0; i< MAX_NUM; i++)
{
// Create a container referencing the BitmapData instance
myContainer = new Bitmap(myImage);
// Add it to the DisplayList
addChild(myContainer);
// Place each container
myContainer.x = (myContainer.width + 8) * Math.round(i % 20);
myContainer.y = (myContainer.height + 8) * int(i / 20);
// Set a specific rotation, alpha, and depth
myContainer.rotation = Math.random()*360;
myContainer.alpha = Math.random();
myContainer.scaleX = myContainer.scaleY = Math.random();
}


      对象池
请尽可能使用对象池。另一个重要优化称为对象池,涉及到不断重复使用对象。在初始化应用程序期间创建一定数量的对象并将其存储在一个池中,例如 Array 或 Vector 对象。对一个对象完成操作后,停用该对象以免它占用 CPU 资源,然后删除所有相互引用。然而,不要将引用设置为 null,这将使它符合垃圾回收条件。只需将该对象放回到池中,在需要新对象时可以对其进行检索。重用对象可减少实例化对象的需求,而实例化对象成本很高。还可以减少垃圾回收器运行的机会,从而提高应用程序运行速
度。以下代码演示对象池技术:
package
{
import flash.display.Sprite;
public final class SpritePool
{
private static var MAX_VALUE:uint;
private static var GROWTH_VALUE:uint;
private static var counter:uint;
private static var pool:Vector.<Sprite>;
private static var currentSprite:Sprite;
public static function initialize( maxPoolSize:uint, growthValue:uint ):void
{
MAX_VALUE = maxPoolSize;
GROWTH_VALUE = growthValue;
counter = maxPoolSize;
var i:uint = maxPoolSize;
pool = new Vector.<Sprite>(MAX_VALUE);
while( --i > -1 )
pool[i] = new Sprite();
}
public static function getSprite():Sprite
{
if ( counter > 0 )
return currentSprite = pool[--counter];
var i:uint = GROWTH_VALUE;
while( --i > -1 )
pool.unshift ( new Sprite() );
counter = GROWTH_VALUE;
return getSprite();
}
public static function disposeSprite(disposedSprite:Sprite):void
{
pool[counter++] = disposedSprite;
}
}
}
SpritePool 类在初始化应用程序时创建新的对象池。getSprite() 方法返回这些对象的实例,而 disposeSprite() 方法释放这些实例。代码允许池容量用尽时可以增长。还可以创建固定大小的池,这样当池容量用尽时将不会分配新对象。尽可能避免在循环中创建新对象。有关详细信息,请参见第10 页的“ 释放内存”。以下代码使用 SpritePool 类检索新实例:

const MAX_SPRITES:uint = 100;
const GROWTH_VALUE:uint = MAX_SPRITES >> 1;
const MAX_NUM:uint = 10;
SpritePool.initialize ( MAX_SPRITES, GROWTH_VALUE );
var currentSprite:Sprite;
var container:Sprite = SpritePool.getSprite();
addChild ( container );
for ( var i:int = 0; i< MAX_NUM; i++ )
{
for ( var j:int = 0; j< MAX_NUM; j++ )
{
currentSprite = SpritePool.getSprite();
currentSprite.graphics.beginFill ( 0x990000 );
currentSprite.graphics.drawCircle ( 10, 10, 10 );
currentSprite.x = j * (currentSprite.width + 5);
currentSprite.y = i * (currentSprite.width + 5);
container.addChild ( currentSprite );
}
}
以下代码在当单击鼠标时,将删除显示列表中的所有显示对象,并在以后的其他任务中重复使用这些对象:
stage.addEventListener ( MouseEvent.CLICK, removeDots );
function removeDots ( e:MouseEvent ):void
{
while (container.numChildren > 0 )
SpritePool.disposeSprite (container.removeChildAt(0) as Sprite );
}
注: 池矢量始终引用 Sprite 对象。如果要从内存中完全删除对象,需要对 SpritePool 类使用 dispose() 方法,从而删除所有剩余引用。
      释放内存
      删除对对象的所有引用以确保触发垃圾回收。在 Flash Player 的发行版中无法直接启动垃圾回收器。要确保将一个对象作为垃圾回收,请删除对该对象的所有引用。请记住,在 ActionScript 1.0 和 2.0 中使用的旧 delete 运算符在 ActionScript 3.0 中有不同的行为。它只能用于删除动态对象的动态属性。
注: 在 Adobe® AIR® 和 Flash Player 的调试版中可以直接调用垃圾回收器。
例如,以下代码将 Sprite 引用设置为 null:
var mySprite:Sprite = new Sprite();
// Set the reference to null, so that the garbage collector removes
// it from memory
mySprite = null;
请记住,当对象设置为 null 时,不必将其从内存中删除。如果系统认为可用内存不是足够低,垃圾回收器可能不会运行。垃圾回收的执行时间不可预知。内存分配(而不是对象删除)会触发垃圾回收。当垃圾回收器运行时,它将查找尚未收集的对象的图形。垃圾回收器通过在这些图形中查找相互引用但应用程序不再使用的对象,从而检测出处于非活动状态的对象。将删除通过这种方式检测到的处于非活动状态的对象。

在大型应用程序中,此进程会占用大量 CPU 并影响性能,还可导致应用程序运行速度显著降低。通过尽量重复使用对象,尝
试限制使用垃圾回收。此外,尽可能地将引用设置为 null,以便垃圾回收器用较少处理时间来查找对象。将垃圾回收看作一项
保护措施,并始终尽可能明确地管理对象生存期。
注: 将对显示对象的引用设置为 null 不能确保冻结该对象。该对象在作为垃圾回收之前,仍将占用 CPU 周期。在将对对象的
引用设置为 null 之前,先确保正确地停用对象。
可使用 Adobe AIR 和 Flash Player 的调试版中提供的 System.gc() 方法启动垃圾回收器。将此设置与 Adobe® Flash®Builder™捆绑还可以手动启动垃圾回收器。通过运行垃圾回收器,可以了解应用程序的响应方式以及是否已将对象从内存中正确删除。注: 如果将某个对象用作事件侦听器,则其他对象可以引用它。如果是这样,先使用 removeEventListener() 方法删除事件侦听器,然后再将引用设置为 null。幸运的是,这样可以立即减少位图使用的内存量。例如, BitmapData 类包括一  个 dispose() 方法。下面的示例将创建一个 1.8MB 的 BitmapData 实例。当前使用的内存增大到 1.8 MB, System.totalMemory 属性返回一个更小的值:
trace(System.totalMemory / 1024);
// output: 43100
// Create a BitmapData instance
var image:BitmapData = new BitmapData(800, 600);
trace(System.totalMemory / 1024);
// output: 44964
然后,手动将 BitmapData 从内存中删除并再次检查内存使用情况:
trace(System.totalMemory / 1024);
// output: 43100
// Create a BitmapData instance
var image:BitmapData = new BitmapData(800, 600);
trace(System.totalMemory / 1024);
// output: 44964
image.dispose();
image = null;
trace(System.totalMemory / 1024);
// output: 43084
尽管 dispose() 方法可删除内存中的像素,但仍必须将引用设置为 null 才可完全释放内存。当不再需要 BitmapData 对象时,要始终调用 dispose() 方法并将引用设置为 null,以便立即释放内存。注: Flash Player 10.1 和 AIR 1.5.2 在 System 类中引入了一个名为 disposeXML() 的新方法。借助此方法,通过将 XML 树作为参数传递,可立即使 XML 对象可用于垃圾回收。

 

原创粉丝点击