Pool

From ActionScriptWiki

Jump to: navigation, search

A pool of objects is something that is very useful because of various reasons.

  • The creation of an object happens only once and the new operator is very expensive.
  • The objects stay in memory and new objects are only created when needed.
  • The Flash Player garbage collection does not have to free the memory for all the objects one may create.

[edit] Point Pool

Every object can be put into a pool. This is a simple example of how the existing Point class could be pooled. The pool is nothing else than a LinkedList of PooledPoint objects. The poolGrowthRate variable is very important because it might be wise to choose a higher or lower value according to the situation.

It is very important to call the dispose() method on a PooledPoint object once it is no longer needed. Otherwise it will not go back into the pool and everything becomes senseless.

package
{
	import flash.geom.Point;
	import flash.utils.getQualifiedClassName;

	class PooledPoint extends Point
	{
		public static function create( x: Number = 0.0, y: Number = 0.0 ): PooledPoint
		{
			var pooledObject: PooledPoint;
			
			if( 0 == _availableInPool )
			{
				var poolGrowthRate: int = 0x10;
				
				/*FDT_IGNORE*/
				CONFIG::Debug
				{
				/*FDT_IGNORE*/
					trace( 'Growing ' + getQualifiedClassName( PooledPoint ) + ' pool by', poolGrowthRate.toString() );
				/*FDT_IGNORE*/
				}
				/*FDT_IGNORE*/
				
				var n: int = poolGrowthRate + 1;
				
				while( --n != 0 )
				{
					_ctorAllowed = true; {
						pooledObject = new PooledPoint();
					} _ctorAllowed = false;
					
					pooledObject._nextInPool = _pool;
					_pool = pooledObject;		 
				}
				
				_availableInPool += poolGrowthRate;
			}
			
			pooledObject = _pool;
			_pool = pooledObject._nextInPool;
			--_availableInPool;
			
			pooledObject.x = x;
			pooledObject.y = y;
			
			return pooledObject;	
		}
		
		private static function release( pooledPoint: PooledPoint ): void
		{
			pooledPoint._nextInPool = _pool;
			_pool = pooledPoint;
			
			++_availableInPool;
		}
		
		private static function verifyCtor(): void
		{
			if( !_ctorAllowed )
				throw new Error( getQualifiedClassName( PooledPoint ) + ' is a pooled class. Use the static create() method instead.', 0x1000 );
		}
		
		private static var _ctorAllowed: Boolean = false;
		private static var _pool: PooledPoint = null;
		private static var _availableInPool: int = 0;
		private var _nextInPool: PooledPoint;
		
		public function PooledPoint( x: Number = 0.0, y: Number = 0.0 )
		{
			verifyCtor();
			super( x, y );
		}
		
		public function dispose(): void
		{
			release( this );
		}
	}
}

[edit] Example Usage

var i: int = 0;
var points: Vector.<PooledPoint> = new Vector.<PooledPoint>();

for( i = 0; i < 0x10; ++i )
{
	trace( 'Creating Point ', i.toString() );
	points.push( PooledPoint.create() );
}

for( i = 0; i < 0x10; ++i )
{
	trace( 'Disposing Point', i.toString() );
	points.pop().dispose();
}

for( i = 0; i < 0x20; ++i )
{
	trace( 'Creating Point ', i.toString() );
	points.push( PooledPoint.create() );
}

[edit] FDT Template

This is a very useful template which instantly adds the functionality of a pool to the current class.

/**
 * Returns a pooled ${enclosing_type} object.
 *
 * @return A pooled ${enclosing_type} object.
 */
public static function create(): ${enclosing_type}
{
	var pooledObject: ${enclosing_type};
	
	if( 0 == _availableInPool )
	{
		var poolGrowthRate: int = ${growthRate};
		
		/*FDT_IGNORE*/
		CONFIG::Debug
		{
		/*FDT_IGNORE*/
			trace( 'Growing ' + getQualifiedClassName( ${enclosing_type} ) + ' pool by', poolGrowthRate.toString() );
		/*FDT_IGNORE*/
		}
		/*FDT_IGNORE*/
		
		var n: int = poolGrowthRate + 1;
		
		while( --n != 0 )
		{
			_ctorAllowed = true; {
				pooledObject = new ${enclosing_type}();
			} _ctorAllowed = false;
			
			pooledObject._nextInPool = _pool;
			_pool = pooledObject;		 
		}
		
		_availableInPool += poolGrowthRate;
	}
	
	pooledObject = _pool;
	_pool = pooledObject._nextInPool;
	--_availableInPool;
	
	//
	// Initialization code goes here ...
	//
	${cursor}
	
	return pooledObject;	
}

/**
 * Puts an object back into the pool.
 *
 * @param pooledObject The object to put into the pool.
 * @private
 */
private static function release( pooledObject: ${enclosing_type} ): void
{
	pooledObject._nextInPool = _pool;
	_pool = pooledObject;
	
	++_availableInPool;
}

/**
 * Verifies if it is allowed to call the constructor of ${enclosing_type}.
 *
 * @throws Error An error is thrown if it is not allowed to call the constructor.
 * @private
 */
private static function verifyCtor(): void
{
	if( !_ctorAllowed )
		throw new Error( getQualifiedClassName( ${enclosing_type} ) + ' is a pooled class. Use the static create() method instead.' );
}

/**
 * Whether or not it is allowed to call the constructor.
 * @private
 */
private static var _ctorAllowed: Boolean = false;

/**
 * A linked list of ${enclosing_type} objects which holds
 * unused ones.
 *
 * @private
 */
private static var _pool: ${enclosing_type} = null;

/**
 * The length of the linked list.
 * @private
 */
private static var _availableInPool: int = 0; 

/**
 * The next element in the linked list.
 * @private
 */
private var _nextInPool: ${enclosing_type};
	
/**
 * Creates a new ${enclosing_type} object.
 *
 * Calling the constructor outside of the <code>create()</code> method
 * will result in an error.
 *
 * @private
 */
public function ${enclosing_type}()
{
	verifyCtor();
}
	
/**
 * Frees all internal references of the current object and puts it back into the pool.
 */
public function dispose(): void
{
	release( this );
}
Personal tools