Search code examples
performanceactionscript-3object-pooling

Universal object pool


I see the benefit of using object pooling, and I also want to combine it with Vectors. But, reading about Vectors, I see that they can only be defined at compile time, meaning a separate pooler class for each of the pooled classes is required. On the other hand, I would like to have a random class instance of a set (all extending a certain class) to be pulled from a pool at runtime, so that I don't exactly know which object pooler is to be called. And, in order to not multiply the code sequences for pooler classes, I want to combine all pools into a UniversalPooler class, which then can serve requests like var foo:SomeClass=UniversalPool.getFromPool(SomeClass);. The question is, how can I implement such a universal pooler performance effectively, using Vectors if at all possible, and probably using random subclass selection?


Solution

  • Yes, unfortunately it's not possible to use Vectors, AS3 has very poor implementation of generics. I use the following pooling based on class map Dictionary and Arrays for objects storage:

    package util.pool
    {
    import flash.utils.Dictionary;
    
    public class ObjectPool
    {
        private var storage:Dictionary = new Dictionary();
    
        public function ObjectPool()
        {
        }
    
        private function createNew(constructor:Class, ...args):Object
        {
            //as3 - (facepalm), why there isn't API to path array of params to constructor
            switch (args.length) {
                case 0:return new constructor();
                case 1:return new constructor(args[0]);
                case 2:return new constructor(args[0], args[1]);
                case 3:return new constructor(args[0], args[1], args[2]);
                case 4:return new constructor(args[0], args[1], args[2], args[3]);
                case 5:return new constructor(args[0], args[1], args[2], args[3], args[4]);
                case 6:return new constructor(args[0], args[1], args[2], args[3], args[4], args[5]);
                case 7:return new constructor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
                case 8:return new constructor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
                case 9:return new constructor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
                case 10:return new constructor(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
                default: throw new Error("too much arguments for constructor, add more switch cases :))");
            }       
        }
    
        public function create(constructor:Class, ...args):Object
        {
            var pool:Array = getOrCreatePool(constructor);
    
            var res:*;
            if(pool.length > 0)
            {
                res = pool.pop();
            }else
            {
                args.unshift(constructor);
                res = createNew.apply(null, args);
            }
    
            return res;
        }
    
        public function destroy(obj:Object):void
        {
            if(!obj) return;
            if(!(obj is Object)) return;
    
            var constructor:Class = obj["constructor"];
            var pool:Array = getOrCreatePool(constructor);
            pool[pool.length] = obj;
        }
    
        public function destroyArr(objs:Object):void
        {
            for each(var obj:Object in objs)
                destroy(obj);
        }
    
        private function getOrCreatePool(constructor:Class):Array
        {
            var pool:Array = storage[constructor];
            if(!pool)
            {
                pool = [];
                storage[constructor] = pool;
            }
    
            return pool;
        }
    }
    }
    

    It can be used as one global pool. And I don't think that there can be much more effective implementation, it's a quite simple feature in general.