Initially I had ten arraycollections declared in a flex module, which I thought, were causing a memory leak. So I separated them in a single class, which I would clean using the "destroy" method I created inside it. Would this work?
I hate the question title excuse me. But I wouldn't write it like "Seducing the garbage collector"
[Bindable]
public class Cfd
{
private static var instance:Cfd = new Cfd();
private var _cfds:ArrayCollection = new ArrayCollection();
// Constructor
public function Cfd(){
if (instance) { throw new Error('Cannot create a new instance. Must use Cfd.getInstance().') }
}
public static function getInstance():Cfd{
return instance;
}
public function get cfds():ArrayCollection{
return _cfds;
}
public function set cfds(value:ArrayCollection):void{
_cfds = value;
}
public function destroy():void{
if(_cfds != null){
_cfds.removeAll();
}
}
}
Whenever you use Singletons like this, you pretty much guarantee a memory leak, because you're probably listening to the ArrayCollection (and maybe items within it) from all over the place. When you explicitly provide a reference to an object through a getter/setter pair, you can add the listener in the setter and removie it when the value is reset.
Check out http://www.developria.com/2010/08/rethinking-addeventlistener-an.html for more on what's happening.
For more on why you should avoid Singletons http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars
Just null the object should do it unless you have listeners attached to it.
cfds = null;
I have never seen [Bindable] used on the class itself, so not sure what you are trying to do there.
package{
public final class Cfd{
private static var instance;
private var _cfds:ArrayCollection = new ArrayCollection();
public function Cfd(singletonEnforcer:MySingletonEnforcer){
if (instance) { throw new Error('Cannot create a new instance. Must use Cfd.getInstance().') }
}
public static function getInstance():Cfd{
if (instance == null)
instance = new MySingleton(new MySingletonEnforcer());
return instance;
}
// don't see a real need for setter/getters here
public static function get cfds():ArrayCollection{
return _cfds;
}
public static function set cfds(value:ArrayCollection):void{
_cfds = value;
}
// I wouldn't even use a destroy on this class since it is a singleton.
// just set the data to null Cfd.cfds = null
public static function destroy():void{
_cfds = null
}
}
}
//this is in Cfd.as but is outside the package block
class MySingletonEnforcer {}
Related
How can I get a reference to my stage without having a Sprite/DisplayObject that is added to the stage already ?
More info: I have a static class that is a utility class and I want it to initialize in static class constructor but I also need the reference to the stage.
public class UtilClass
{
trace("init: " + stage);
}
First thing that is called in my AS-3 apps is the constructor of my main Sprite/DisplayObject and it has access to the stage. So the stage exists at that point. Then I call utility methods of my UtilClass. Now I want it to initialize by itself on the first use (when stage is already in existance).
I want to know if stage object can be accessed from anywhere without being initialized from outside of the utility class.
Edit:
public class SimpleSprite extends Sprite
{
public static var aaa:int = 12;
public static function test():void
{
trace("here I am");
}
trace(aaa, Capabilities.screenResolutionX+", "+Capabilities.screenResolutionY);
test();
}
The stage reference is available in your MainTimeline or Main instance, depending on platform. You can add code there to pass that reference to other classes should you need it. The class should have a method (static, in your case) that'll accept a Stage parameter and store it somewhere inside the class.
public class UtilClass {
private static var theStage:Stage=null;
public static function initialize(s:Stage):void {
if (theStage) return; // we're initialized already
theStage=s;
}
// once you call this, you can "trace(theStage)" and get correct output
// other methods can also rely on theStage now.
}
Then you call UtilClass.initialize(stage); and you're set.
You will need to initialise your UtilClass and pass the stage reference. I recommend you to have a Class only for 'manage' Stage reference.
You could try something like this (just a quick example):
public class StageReference
{
public static const STAGE_DEFAULT:String = 'stageDefault';
protected static var _stageMap:Dictionary;
public static function getStage(id:String = StageReference.STAGE_DEFAULT):Stage
{
if (!(id in StageReference._getMap()))
throw new Error('Cannot get Stage ("' + id + '") before it has been set.');
return StageReference._getMap()[id];
}
public static function setStage(stage:Stage, id:String = StageReference.STAGE_DEFAULT):void
{
StageReference._getMap()[id] = stage;
}
public static function removeStage(id:String = StageReference.STAGE_DEFAULT):Boolean
{
if (!(id in StageReference._getMap()))
return false;
StageReference.setStage(null, id);
return true;
}
protected static function _getMap():Dictionary
{
if (!StageReference._stageMap) StageReference._stageMap = new Dictionary();
return StageReference._stageMap;
}
}
When you start your application (Main Class or where you start to include your logic)
StageReference.setStage(stage);
And when you need to get the stage reference
trace('Checking the Stage: ', StageReference.getStage());
So I need to access information from my document(Main.as) class.
I tried making this easy by saving a static instance of my Main class.
private static var _instance:Main;
public static function get instance():Main { return _instance; }
public function Main() {
_instance = this;
}
Then when in another class I try to use this I get a null reference error.
public function InputController():void {
main = Main.instance;
main.stage.addEventListener(KeyboardEvent.KEY_DOWN, OnKeyPress);
main.stage.addEventListener(KeyboardEvent.KEY_UP, OnKeyRelease);
}
I get an error on the main.stage.addEventListener lines.
If you have timeline coding, you can instantiate your static variable with _instance=this (should be accessible, as main timeline is a part of document class) at the first keyframe, and reference there from elsewhere via property.
You are trying to return an instance without instantiating it. Use the following code for instantiating the class first.
private static var _instance:Main;
public static function get instance():Main
{
if(_instance == null)
_instance = new Main();
return _instance;
}
Actually in my Flex Application have Some Popup windows and i want take some values in this Popup Window But The Values are Comming NULL
So how to Make a PopUp Window as Global? Because we are Using the values Globally.
Please Give Suggestion...
Edit
I'm Edit with some code...
Main.mxml(Main Aplication), Demo1.mxml(PopUpWindow), Demo2.mxml(PopUpWindow)
Now in Demo1.mxml have variable like...
[Bindable]private var arrayC:ArrayCollection=new ArrayCollection();//Hear Some value their.
NOw i want Use arrayC in Demo2.mxml then ..
public var variable1:Demo1=new Demo1();
var ac:ArrayCollection = new ArrayCollection();
ac = variable1.arrayC;
But hear ac contain Null Value Why?
Then,Now i'm Thinking Demo2.mxml(PopUpWindow) is Converting To Global Scope so it's value Used in Any Where .
Null because of you are tried create new instance so that each instance having their own state.
Also i bet you can't access arrayC ArrayCollection variable declared as private so you can't acccess.
Need to follow few steps
[Bindable]public var arrayC:ArrayCollection=new ArrayCollection(); //Make public variable
Use Singleton Class for you application
package com.foo.bar {
public class Model {
private static var instance : Model;
public function Model( enforcer : SingletonEnforcer ) {}
public static function getInstance() : Model {
if (!instance) {
instance = new Model( new SingletonEnforcer() );
}
return instance;
}
public var arrayC:ArrayCollection = new ArrayCollection();
}
}
class SingletonEnforcer{}
For more details Singleton pattern
private var popup:Demo1;
popup = PopUpManager.createPopUp(this,Demo1,true) as Demo1;
popup.arrayC = Model.getInstance().arrayC; // Here set value from Model class
PopUpManager.centerPopUp(popup);
Suppose you tried to access demo1.mxml arrayC variable in Demo2.mxml
var demo1_arrayC = Model.getInstance().arrayC;
Note that you can access arrayC arraycollection anywhere in your application like Demo2.mxml,Demo3...etc.
But better we have to avoid Singleton Class (unit test diffcult ..etc).
If you are using the values Globally, then no matter what you mean by the "Make a PopUp Window as Global", I strongly suspect that you would be best served by a singleton event dispatcher.
package com.example.demo.models {
import flash.events.IEventDispatcher;
import flash.events.EventDispatcher;
[Bindable]
class MyGlobalStuff extends EventDispatcher {
public var someGlobalValue:*;
private var _instance:MyGlobalStuff;
public function MyGlobalStuff (lock:SingletonLock, target:IEventDispatcher=null) {
super(target);
if(!(lock is SingletonLock)) {
throw(new Error("MyGlobalStuff is a singleton, please do not make foreign instances of it"));
}
}
public static function getInstance():MyGlobalStuff {
if(!_instance) {
_instance = new MyGlobalStuff (new SingletonLock());
}
return _instance;
}
}
}
class SingletonLock{}
The idea is this: that you would bind in your popup to
{myStuffModel.someGlobalValue}
myStuffModel would be initialized in your mxml as:
protected var myStuffModel:MyStuffModel = MyStuffModel.getInstance();
then in any other class throughout your application you can bind to or access the EXACT same data via the singleton model.
I am looking to declare a GLOBAL VAR in the main time line.
Then I need to access that GLOBAL VAR from another externally loaded SWF's.
QUESTIONS:
How do I create the global var in the main timeline?
How do I access that var in externally loaded swf files?
First, you shouldn't use any global/static state. In your situation this is even more true, because Singletons are a royal pain in the butt across different applicationDomains.
Instead, you should use something called Dependency Injection. Think of your little swfs as starving orphans. When they have loaded, they don't run up to your main swf and pick its pockets. Instead, the main swf magnanimously presses money into their little hands.
So, how do we make this happen? One way is that we could compile a reference to their Document class(es) into the main swf, and then we could set a variable that the Class exposes. However, this can get pretty heavy and isn't really necessary.
Instead, you can write something called an Interface, which defines the "idea" of an orphan.
It might look something like this:
public interface IOrphan {
function get alms():Number;
function set alms(value:Number):void;
}
Note that you have to use getters and setters with Interfaces, because you can't use them to define vanilla variables. However, that's going to work out great for our actual Orphan:
public class Oliver implements IOrphan {
private var _alms:Number;
private var _totalAlms:Number;
public var tf:TextField;//put this on stage and allow Flash to populate automatically
public function get alms():Number {
return _alms;
}
public function set alms (value:Number):void {
_alms = value;
_totalAlms += _alms;
updateAlmsMessage();
}
private function updateAlmsMessage():void {
tf.text = 'That was a donation of ' + _alms + '.\n'
'I now have ' _totalAlms + '.\n'
'Please, sir, can I have some more?';
}
}
Now, all you need to do is populate that variable on load. There are several ways you can do this, such as watching the stage for IOlivers to be loaded, or you could be more direct about it:
private function loadSwf(url:String):void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
var request:URLRequest = new URLRequest(url);
loader.load(request);
addChild(loader);
}
private function completeHandler(e:Event):void {
((e.target as LoaderInfo).content as IOrphan).alms = .25;
}
If these are variables that you only want to set once and will never change, you can just create a class that holds static constants.
package
{
public class Env
{
public static const WHATEVER:String = "Whatever!";
public function Env()
{}
}
}
Then you could access them later in your program like so:
trace(Env.WHATEVER);
However, if you want global variables that can change, I like to handle this by using a singleton class.
package
{
import flash.events.EventDispatcher;
public class Control extends EventDispatcher
{
//---------------------------------------
// PRIVATE & PROTECTED INSTANCE VARIABLES
//---------------------------------------
private static var _instance:Control;
//---------------------------------------
// PUBLIC VARIABLES
//---------------------------------------
public var whatever:String = "Whatever";
//---------------------------------------
// PUBLIC METHODS
//---------------------------------------
public static function get instance():Control
{
return initialize();
}
public static function initialize():Control
{
if (_instance == null)
{
_instance = new Control();
}
return _instance;
}
//---------------------------------------
// CONSTRUCTOR
//---------------------------------------
public function Control()
{
super();
if (_instance != null)
{
throw new Error("Error:Control already initialised.");
}
if (_instance == null)
{
_instance = this;
}
}
}
}
The difference here is that you need to grab the instance of your singleton before you can get to what's inside it. It'd look a little bit like this.
private var _control:Control = Control.instance;
// Reading a global variable
trace(_control.whatever);
// Change a global variable
_control.whatever = "Foobar!";
So whenever you change "whatever", that variable will change for all loaded SWFs. If you want to be really fancy about it, you could use getters/setters in your singleton rather than simple public variables.
I apologize in advance if my question is not clear, because I don't know how to put this.
What I am trying to do is to reduce few lines of repeated code by implementing various OOP methods/concepts.
The problem
I have few set of of classes which has initialization process. So, I am implementing an init() method in all those classes. From the calling class (main), these objects will be instantiated and init() method of each object is called in the the order and call some other process after all of them are initialized.
Something like this
public function mainClass(){
_obj1 = new Class1();
_obj1.init();
_obj2 = new Class2();
_obj2.init();
_obj3 = new Class3();
_obj3.init();
doSomething();
}
Well, its not a big deal, but some of the classes' init() methods are asynchronous and I need to add an event listener to get notified when they have finished initialization.
I tried that by extending EventDispatcher for each of those classes and dispatch event and handle it. I even implemented a logic to handle multiple asynchhnous calls by maintaining a counter.
It will be a painful job for me whenever I need to add a new class. I thought I could untilize OOP and reduce and simplify the code.
So I came up with some thing like this, which is currently not possible (abstract class).
abstract class Initializable
{
private var _callBack:Function;
//implement initializaton process in this method
function init(callback:Function=null):void;
protected function get callback():Function{
return _callBack;
}
protected function set callback(func:Function):void{
_callBack = func;
}
protected function onComplete():void{
if (_callBack){
_callBack(this);
}
}
}
This is the main problem for me, as you know abstract class is not allowed in AS3, and the "this" refers to the Initializer class but not its subclass I guess.
This is what I am asking for your help (for the hack)
I need it very much to make my system design simple and flexible, because I can extend the solution to allow mass synchronous initialization which will allow to easily queue up all objects in the order and call init() one after the other in the order in which they are added.
The mass initializer which takes care of handling the asynchronous job
public class MassInitializer
{
private var _objList:Array; //holds objects
private var _callBacks:Array;
private var _onComplete:Function;
public function MassInitializer()
{
_objList = new Array();
}
public function add(obj:Initializable,callback:Function=null):void{
_objList.push(obj);
_callBacks.push(callback);
}
public function init():void{
for (var i:int = 0;i < _objList.length;i++){
_objList.init(this);
}
}
private function onProgress(obj:Initializable):void{
//do updates here
for (var i:int;i<_objList.length;i++){
var obj:Initializable = _objList[i];
var fun:Function = _callBacks[i];
//update progress
if (fun){
fun(obj);
}
_callBacks.splice(i,1);
_objList.splice(i, 1);
}
if (_objList.length == 0){
onComplete();
}
}
private function onComplete():void{
_onComplete(this);
}
}
the main (manager/caller) class (ClassA, ClassB are subclasses of Initialzable class)
public class MainClass
{
private var _obj1:ClassA;
private var _obj2:ClassB;
public function MainClass()
{
_obj1 = new ClassA();
_obj2 = new ClassB();
}
public function init():void{
var initManager:MassInitializer = new MassInitializer();
initManager.add(obj1);
initManager.start();
}
}
probably I am trying to (or want to )implement an observer pattern, but I don't want to confuse you by saying it in advance. Oops I said it? please ignore.
You can emulate abstract classes in ActionScript by enforcing method overrides: Just throw an error if the "abstract" method is called. I like to also implement an interface, but that's not a must, of course:
public interface Initializable
{
function init (callback : Function = null) : void;
function get callback () : Function;
function set callback ( callback : Function ) : void;
}
public class AbstractInitializableImpl implements Initializable
{
private var _callBack:Function;
protected function init(callback:Function=null):void {
throw new Error ("You must implement the init() method!");
}
protected function get callback():Function {
return _callBack;
}
protected function set callback(func:Function):void {
_callBack = func;
}
protected function onComplete():void {
if (_callBack){
_callBack(this);
}
}
}
It's not a 'hack'. It's also very simple. Make each subclass implement an interface, instead of extend an abstract class.
Here is the adobe reference on AS3 interfaces.