Using Trace Statements to Force a Debug Mode.

I discovered a way to abuse Flash and ActionScript 3.0 that I think could be very useful for many cases.

  • Put debug or testing-only code inside of trace statements (ie:  trace(debugMode = true); )
  • On publish and deployment, check the box labelled ‘omit trace statements’

What’s the benefit? Simple – you can write 10,000 lines of code for debugging purposes, and publish exactly zero of them, with a simple tick of a checkbox. The code footprint actually decreases too – your debug swfs will be many kB larger than the final published ones. Which means code that was never meant to be seen by users will stay that way.

Some examples:

  • Override a domain-lock only while debugging
  • Code cheats for local use that cannot be exploited online (since the related functions will be empty)
  • Prevent your debug-console with private, secure variables from ever being released, even in part
  • Wrap calls to the initialization of entire classes in trace statements (like development tools and level builders), then prevent them from being compiled with the SWF.
  • You can build a massively secure Flash file that is specifically designed to work only on a secure server, and with no changes to configuration or code, have a massive local testbed that works differently while you’re building and debugging it.

I intend to use it for the domain-lock method for now. My files will always work locally and on my server, but break anywhere else. I like that.

Adding a Hover Button (or OverState) to Starling’s Button Class

Came up with a little micro class that extends the Starling Button class, adding support for Hover events – good for applications deployable on both desktop and touch devices.

Simple summary: it replaces the Button’s upState Texture on hover, and then resets it again on hover end.


package {
import starling.display.Button;
import starling.display.DisplayObject;
import starling.events.TouchEvent;
import starling.textures.Texture;

/**
* The HoverButton Class
* Essentially adds an overState to the Starling Button class.
* @author Tony Downey
*/
public class HoverButton extends Button {

private var mUpState:Texture;
private var mOverState:Texture;
private var mIsOver:Boolean;

public function HoverButton(upState:Texture, text:String="", downState:Texture=null, overState:Texture=null) {
super(upState, text, downState);
mOverState = overState;
mUpState = upState;
addEventListener(TouchEvent.TOUCH, onTouchCheckHover);
}

/** Checks if there is a parent, an overState, and if the touch event is hovering; if so, it replaces the upState texture */
private function onTouchCheckHover(e:TouchEvent):void {
if (parent && mOverState && upState != mOverState && e.interactsWith(e.currentTarget as DisplayObject)) {
removeEventListener(TouchEvent.TOUCH, onTouchCheckHover);
parent.addEventListener(TouchEvent.TOUCH, onParentTouchCheckHoverEnd);
upState = mOverState;
}
}

/** Checks if there is a parent, an overState, and if the touch event is finished hovering; if so, it resets the upState texture */
private function onParentTouchCheckHoverEnd(e:TouchEvent):void {
if (parent && mOverState && ! e.interactsWith(e.currentTarget as DisplayObject)) {
parent.removeEventListener(TouchEvent.TOUCH, onParentTouchCheckHoverEnd);
addEventListener(TouchEvent.TOUCH, onTouchCheckHover);
upState = mUpState;
}
}

/** The texture that is displayed while the button is hovered over. */
public function get overState():Texture { return mOverState; }
public function set overState(value:Texture):void{ if (mOverState != value) mOverState = value; }

/** The texture that is displayed when the button is not being touched. */
override public function get upState():Texture { return mUpState; }
override public function set upState(value:Texture):void {
if (mOverState != value) mUpState = value;
super.upState = value;
}

}

}

Flash, Memory Leaks, and the Garbage Collector.

This was important enough that I wanted to echo Andy Moore’s and Tom Mason’s findings.

If you don’t know much about the Garbage Collector in Flash, it works like this: at a completely irregular interval, usually tied in with the need for more memory, Flash or AIR will sweep through the objects in its memory trying to find orphaned objects. If all references to the object are null, and all event listeners that reference the object are gone, then it’s cued up for garbage collection and the memory is freed up. Great.

As a nice bonus feature, the children of a more complicated object that’s marked for garbage collection will also get deleted. That means when a class dies, you don’t need to explicitly null out all internal references – Flash will handle this for you.

Now, here’s the trick. The garbage collector has an undocumented behaviour, which is incredibly frustrating: large orphaned objects, over a unknown memory footprint or level of complexity, will stay in memory.

Here’s a practical example: I have an AIR application which loads in external swf files. The loader class has a function named unloadAndStop() – this function is documented to be a fantastic way to handle memory, as it removes the swf from memory and nulls out all references to any internal event listeners and such. And it runs the garbage collector for you!

And this is reflected in calls to System.totalMemory and System.freeMemory. By all appearances, the object has been removed from memory and you’re a great programmer.

Do that process fifty times in a row and call System.privateMemory. It’s huge.

System.privateMemory calls for the memory allocated to your application and whatever it’s running in – if it’s a swf, it will return the memory allocated to the browser or projector or whatever. If it’s an AIR application, it will return the memory allocated to your app and the AIR wrapper.

That’s where the memory sink is hidden. Nulling out references to massive objects will remove them from the application’s memory, but not the wrapper – the object is seemingly cached in local active memory.

Lesson learned: pool your objects, and if you have to load in hundreds of megabytes of data, you better have a few gigabytes of extra ram and a self-rebooting function built in, because sooner or later you will run out of memory.