views:

72

answers:

1

Okay by deleting correctly I mean am I actually getting rid of the instance or is it just not being drawn anymore? I should mention that I'm trying to delete the instance from within its own class, that is it deletes itself. It 'works' in that the square it draws no longer appears on the screen but again I'm not sure if it's really gone or just not being drawn. Anyway here's the class:

package
{
    import flash.display.*;
    import flash.events.*;
    public class OBJECT_bullet_1 extends Sprite
    {
        public var X:int = 0;   public var Y:int = 0;
        public var Y_SPEED:int = 5;
        public var DEPTH:int = 9;
        public var CONTAINER:Sprite = new Sprite();
        public function CREATE(CONTAINER:Sprite,X:int,Y:int):void
        {
            this.CONTAINER = CONTAINER;
            CONTAINER.stage.addEventListener(Event.ENTER_FRAME,STEP);
            this.X = X;     this.Y = Y;
            DRAW();
        }
        public function STEP(event:Event):void
        {
            this.graphics.clear();
            Y -= Y_SPEED;
            if (Y < 20) {Y = 300; CONTAINER.removeChild(this); CONTAINER.stage.removeEventListener(Event.ENTER_FRAME,STEP); CONTAINER.(delete this); CONTAINER = null; return;}
            DRAW();
        }
        public function DRAW():void 
        {
            this.graphics.beginFill(0xCCCC00,1);
            this.graphics.drawRect(X - 2,Y - 2,4,4);
            this.graphics.endFill();
            CONTAINER.addChild(this);
        }
    }
}

The part I'm concerned about is in the STEP function when it checks to see if Y < 20. You'll notice that it does several things afterwords. Am I deleting it correctly? If so is there anything I am doing to delete it that I don't need to?

+3  A: 

Yes to both questions. To ensure an object is deleted, all you have to do is remove all references to it. The child reference and event callback are the only ones the above code is aware of, and you have taken care to remove them both. Nullifying your own container reference is unnecessary, as is whatever you think CONTAINER.(delete this) does.

There are some other significant problems with your supplied code. I made some improvements and heavily commented all changes to explain why I made them.

// You should avoid using the default package.  Using the default package
// can make it difficult later on if you start having naming conflicts.
package com.stackoverflow.example {

    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Point;
    import flash.utils.getTimer;

    // Class names are spelled in CamelCase by convention.  Also, note
    // that "Object" has a special meaning in AS3 so you should avoid
    // using it to refer to anything else.  I used here "Entity" instead.
    public class EntityBullet1 extends Sprite {
        // ALLCAPS when used are reserved for static const names.
        // A good use of static consts is to store "magic numbers".
        public static const DEFAULT_COLOR:uint     =  0xCCCC00;
        public static const DEFAULT_SPEED_X:Number =  0;
        public static const DEFAULT_SPEED_Y:Number = -100;
        public static const DEFAULT_SIZE:Number    =  4;

        // I'm calculating the time between frames for smoother movement.
        public var lastTime:int;
        public var color:uint = DEFAULT_COLOR;
        public var size:int   = DEFAULT_SIZE;

        // Instead of separate x and y vars, you can use the Point class.
        public var pos:Point;
        public var speed:Point;

        // Instead of a "create" method do all creation inside the constructor!
        public function EntityBullet1(x:Number = 0, y:Number = 0) {
            pos = new Point(x, y);
            speed = new Point(DEFAULT_SPEED_X, DEFAULT_SPEED_Y);

            // You don't need the parent container to access the ENTER_FRAME
            // event.  Every DisplayObject has its own.  Much simpler.
            addEventListener(Event.ENTER_FRAME, firstStep); 
        }

        public function draw():void {
            // Keep all drawing inside the draw function.  Previously,
            // clear() was being called inside the step method.
            graphics.clear();
            graphics.beginFill(color);
            graphics.drawRect(pos.x - size/2, pos.y - size/2, size, size);
            graphics.endFill();
        }

        // On the first frame, the field "lastTime" is still uninitialized.
        // This method initializes it to the current time and hands off 
        // future events to the proper step() method.
        public function firstStep(event:Event):void {
            removeEventListener(Event.ENTER_FRAME, firstStep);
            addEventListener(Event.ENTER_FRAME, step);
            lastTime = getTimer();
            step(event);
        }

        public function step(event:Event):void {
            // To move at a fixed rate regardless of how fast the framerate is,
            // you need to calculate the time delta.
            var cur:int = getTimer();
            var delta:Number = (cur - lastTime) / 1000.0;
            lastTime = cur;

            // Position equals velocity times time.
            pos.x += speed.x * delta;
            pos.y += speed.y * delta;

            draw();

            // Note that all DisplayObjects already have references to their
            // parent containers called "parent"!
            if (pos.y < 20) {
                if (parent != null) parent.removeChild(this);
                removeEventListener(Event.ENTER_FRAME, step);
            }
        }
    }

}
Gunslinger47
Above and beyond, +1
Tegeril
My code still has a little problem with it, but to address it properly I'd have to explain how weak references work. I'll leave it as it is.
Gunslinger47
@Gunslinger47. +1. Nice answer. I don't quite see the little problem, though.
Juan Pablo Califano
@Juan: If this entity is removed from its container from outside of its class, then the ENTER_FRAME event won't be removed. It will continue being called every frame, and it'll never be garbage collected. (until it runs its course that is) Also consider if you wanted to create an entity, but not immediately add it, or if you'd like to reuse an entity by reinserting it after it removes itself. These are small concerns, but they can be resolved by using weak references to ADDED and REMOVED events in the constructor: http://pastebin.com/bfaA9vAx
Gunslinger47
@Gunslinger47. I agree on using added and removed events to manage the state of the object. It's a simple approach that makes sense and works in many cases. Though I'm sure you don't need to use weakReferences here (since you are listening "on yourself"). Also, leaving a ENTER_FRAME running without need is bad design, if you ask me, but in this case it won't prevent your object from being collected. This would be the case if you were using a Timer though. If you added a strong reference listener to a timer, it won't be collected unless the timer stops or the listener is removed.
Juan Pablo Califano
(cont...) Check out this quick sample. http://gist.github.com/531518. You'll see the count in the dict goes to 0 after the GC runs (you can also check this with Flex Profiler). Now, change the ENTER_FRAME to a timer, make sure it never stops and you added a strong-ref listeners, and then indeed, you have a leak.
Juan Pablo Califano
@Juan: I put a trace inside the step method and it kept going forever, despite being removed from the board. I had a call to `System.gc()` but I guess it wasn't sufficent. I put garbage collection on a reoccuring timer instead and it went away as it should. So, you're right. No need for weak references for your own listeners.
Gunslinger47
@Gunslinger47. Yes, except for Timers... System.gc() has it quirks. Sometimes you have to call it twice in a row or with a timer like you did. Since one will only call gc() manually for debugging or testing anyway, it's not such a big deal, I guess. Anyway, I would like to stress that removing listeners *is* important, because whether they cause a leak or not, the consume resources (and will continue to do it since they are collected). The only exception to the former, IMO, are ADDED_TO_STAGE and REMOVED_FROM_STAGE listeners where the dispatcher and the listener are the same object.
Juan Pablo Califano
Um so great answer, still very new to the language so yeah there are quite a few quirks with my code. But again thanks.
@xxxx1101xxxx. At this point, you should consider marking this answer as accepted...
Juan Pablo Califano
Sorry it took so long for me to accept. But really this is a great answer