flash.utils.getDefinitionByName vs. ApplicationDomain's class definition methods - actionscript-3

What's the difference between these two method groups in terms of the set of classes they work with (i.e. ApplicationDomain's class definition set vs the set of class definitions getDefinitionByName uses)?
ApplicationDomain. getDefinition / hasDefinition / getQualifiedDefinitionNames (Flash Player 11.3+ only, undocumented)
getDefinitionByName
It's clear that there's an application domain hierarchy and that definitions may be visible in some app domains and not others. For example, would ApplicationDomain.getDefinition return a definition that is not defined in the given app domain but is accessible from it? (e.g. if the domain is a child domain and we're looking up a definition defined in the parent?) The documentation for ApplicationDomain just says "Loaded classes are defined only when their parent doesn't already define them." but it also says "(ApplicationDomains) allow multiple definitions of the same class to exist and allow children to reuse parent definitions."
The documentation also indicates that getDefinitionByName returns class definitions, whereas ApplicationDomain.getDefinition will return namespace and function definitions in addition to class definitions.
Assuming I'm only interested in class definitions, what ApplicationDomains does getDefinitionByName search? (e.g. all domains, the current/caller domain only, or any domains accessible to the caller?)
This initial test is confusing:
import flash.system.ApplicationDomain;
var d:ApplicationDomain = new ApplicationDomain( ApplicationDomain.currentDomain ); //child of current domain
trace(ApplicationDomain.currentDomain.hasDefinition("flash.display.DisplayObject")); //true
trace(ApplicationDomain.currentDomain.getQualifiedDefinitionNames().length); //1 (the main timeline class definition only: Untitled_fla::MainTimeline)
trace(d.hasDefinition("flash.display.DisplayObject")); //false
In the above test, on the one hand, getQualifiedDefinitionNames reports that only the main timeline class is defined in the current app domain, yet getDefinition returns true for DisplayObject, indicating it reports the existence of definitions in the parent (system) domain, yet the final trace on the grandchild domain contradicts that by returning false.
ApplicationDomain.currentDomain.parentDomain also returns null, which directly contradicts the following documentation statements: "The system domain contains all application domains, including the current domain..." and "Every application domain, except the system domain, has an associated parent domain. The parent domain of your main application's application domain is the system domain."
The contradiction is very apparent here, where currentDomain has the definition, but when you create a child domain and access the parent, which should be currentDomain, it suddenly reports that it doesn't contain the definition:
trace(ApplicationDomain.currentDomain.hasDefinition("flash.display.DisplayObject")); //true
trace((new ApplicationDomain( ApplicationDomain.currentDomain )).parentDomain.hasDefinition("flash.display.DisplayObject")); //false! why?

This page is quite comprehensive: http://www.senocular.com/flash/tutorials/contentdomains/?page=2 I've managed to solve a couple mysteries, but the basic question outlined above (particularly concerning the scope of getDefinitionByName) still stands. I just wanted to post an answer for what I was able to resolve.
Retreiving the parentDomain returns null if the parent is the system domain. So although the parentDomain is the system domain, the parentDomain property returns null anyway. That's just the way it is. Unfortunately, that makes the system domain inaccessible, for example, for class enumeration through getQualifiedDefinitionNames.
Concerning my initial test, it seems that constructing a new ApplicationDomain creates a dead object until a SWF is actually loaded under that domain. For example, creating a child domain of the current domain and calling hasDefinition on it will return false, but if you assign that very same instance to a loader context an pass it to Loader.load, once the load completes, you can call hasDefinition on the instance that originally returned false, and it will return true instead. So you can construct an ApplicationDomain with a parent, but it won't really function until it's being actively used.
var d:ApplicationDomain = new ApplicationDomain( ApplicationDomain.currentDomain ); //child of current domain
trace(d.hasDefinition( "flash.display.DisplayObject" )); //false for now...
var l:Loader = new Loader();
l.load(new URLRequest( "any.swf"), new LoaderContext( false, d ) );
l.contentLoaderInfo.addEventListener( Event.COMPLETE, completed, false, 0, true );
function completed(e:Event ):void
{
trace(d.hasDefinition( "flash.display.DisplayObject" ); //...and now it's true.
}
So it would seem that ApplicationDomain.getDefinition does report classes in the parent, grandparent, etc. domains, but it will only do so after the new ApplicationDomain instance has been activated through loading something into it.
Also, ApplicationDomain instances may refer to the same application domain, but they cannot be directly compared. For example, (ApplicationDomain.currentDomain == ApplicationDomain.currentDomain) is false.

Related

method .attachMovie() is no longer supported .Flash to AS3 conversion

I am completing an online tutorial and manipulating it suit my website. I've come across this code...
`// Create a menu item movie clip in the menu_mc instance on the main timeline
// for each item element offsetting each additional further down the screen
var item_mc = menu_mc.attachMovie("movieitem","item"+item_count, item_count);
item_mc._x = item_count * item_spacing;
item_count++;`
The following line gives me a problem (the method is no longer supported)
var item_mc = menu_mc.attachMovie("movieitem","item"+item_count, item_count);
How can i achieve this?
I've tried the following with no joy. message too many arguments?
var mItem:movieitem = new movieitem;
var item_mc = menu_mc.addChild(mItem,mItem+item_count, item_count);
addChild() only accepts 1 argument, which is the display object itself. Also, it looks like you're missing brackets when you create your object and by convention, class names are capitalised.
var mItem:movieitem = new movieitem();
Edit based on my comment
Looking at the documentation for attachMovie() for AS2 (wow, been awhile since I've looked at this), it takes in 3 arguments:
id:String, name:String, depth:Number
Now the id is used to grab a movieclip from the library. This is no longer needed as you've already created a movieclip object from your library in the line before:
var mItem:Movieitem = new Movieitem();
The second argument name is used to create a unique instance name for the created moviclip from the library. You don't really need this. In the line where you create the movieclip (see above), you already have a unique reference you can use to access the movieclip. Interestingly, attachMovie() also returns a reference -I've never ever found a use for the instance names given with the 'name' argument. I just use the reference returned to access it, which you are already doing.
The third argument depth determines which depth the movieclip is placed at. In your case, I am guessing that ' item_count' is just a number that increases, which effectively puts that movie clip at the highest depth when that line is executed. By default, addChild() will automatically do this for you and put the display object (your movieclip) at the highest depth within the parent at the time it is added. So, unless you wanted it at a specific depth/overlapping order, you don't really need to pass this in either. If you did want to add something at a specific depth, look at addChildAt()
Hence as mentioned before, you can just pass in the reference to your movieclip/display object in to addChild().

Using retain and release for Objects

Are there any general guide lines for using retain and release for objects in cocos2d-X ? When creating objects in a function, is it true that the functions memory is cleaned up the second the function returns. When a object is created, calling the retain function of the object, will retain object beyond the function return ?
Kind Regards
Generally in c++ you have this behaviour:
void foo() {
Object a;
Object *pA = new Object();
(…)
}
This would result in a being destroyed automatically at function end, as it was allocated on stack. The *pA would not get destroyed, as it was allocated on the heap (thus, you only loose the reference to it, but the object itself still lives).
Cocos implements a thing called "Automatic Reference Counting" : each CCObject has a reference counter and two methods retain() and release(). The way this works is, that every time you create an object, it gets registered in cocos structers (CCPoolManager). Then with every frame (between them being drawn) there is a maintenance loop which checks the reference counter of all objects : if it is 0 this means (to cocos) that no other objects reference it, so it is safe to delete it. The retain count of an object is automatically incresead when you use this object as an argument for an addChild function.
Example :
void cocosFoo() {
CCSprite *a = CCSprite::create(…);
CCSprite *b = CCSprite::create(…);
this->addChild(b);
}
What happens here is this :
Two CCSprites are created, cocos knows about them.
The b sprite is added to this object (say a CCLayer)
The function ends, no objects are destroyed (both of them being on heap).
Somewhere between this and next frame, the maintanance gets run. Cocos chcecks both sprites and sees that a has reference count == 0, so it deletes it.
This system is quite good, as you don't need to worry about memory management. If you want to create a CCSprite (for example), but not add it as a child yet, you can call retain() on it, which will raise its reference counter, saving it from automatic deletion. But then you'd have to remember about calling release() on it (for example, when adding it as a child).
The general things you have to remeber about are :
Each call to retain() by you needs to be paired with release().
You generally shouldn't delete CCObjects yourself. If you feel that you need to, there is a conveniece macro : CC_SAFE_DELETE(object)
So to answer your questions in short :
Are there any general guide lines for using retain and release for objects in cocos2d-X ?
Yes, you should generally not need to do it.
When creating objects in a function, is it true that the functions memory is cleaned up the second the function returns.
Answer to this is the whole text above.
When a object is created, calling the retain function of the object, will retain object beyond the function return ?
Yes, as will adding it as a child to another (retained in any way) object.
Here is the thing,
cocos2dx has an autorelease pool which drains the objects which have retain count=0 which is a variable to keep in check the scope of the cocos2dx object.
Now when you create new object using the create method it is already added to the autorelease pool and you don't need to release it or delete it anywhere , its like garbage collector in java, takes care of garbage objects behind your back.
But when you create new object using 'new' you definitely need to release it in its destructor or after its use is over.
Second thing,
when your object is added to the autorelease pool but you need it somewhere else you could just retain it , this increments its retain count by one and then you have to manually release it after its use is over.
Third Thing,
Whenever you add child your object it is retained automatically but you don't need to release it rather you remove it from the parent.

AS3 create a variable in root from within a function

I have a fairly big swf right now with a bit of coding already. Most vars are created in the root, but now I have a problem.
I want to reload the flash swf (reset), and for that, I need to create a function that destroys all the vars and another one that creates them. At the moment, I have a javascript function that reloads the page, but that really isnt a good solution.
The problem is that when I create a var inside a function, it doesn't get created in "MovieClip(root)", and instead is only related to the function, thus rendering my swf unable to work.
Is there a way to create vars in MovieClip(root) from within a function? Or is there an alternative to what I'm trying to do?
EDIT: Added some example code.
function SetVar():void{
var test:String= new String("foobar");
}
SetVar();
trace(test);
...and the output is:
Scene 1, Layer 'Layer 1', Frame 1, Line 7 1120: Access of undefined property test.
Which is normal, because the "var test" is not global, so it was lost when the function ended. I want to make it so the function "SetVar()" adds the vars to the root, or global.
You need to read up on how scope works.
Basically:
An object declared within another object (be it a Class, Function, Object, or Loop), is only available within that specific object or loop iteration.
Object scope is inherited by children, not by parents. So a function within a class has access to an object declared within that class, but a class does not have access to an object declared within a function
A parent (or any other object) can access objects declared within child classes, but only if it is a public object
So looking at those basic rules (they are very, very basic. If you are just starting out, I urge you to do some proper research into object scope in OOP. It is the basis of everything you will do in dozens of languages), you are declaring an object in a function and trying to access it from outside that function. This breaks Rule #1 from above.
Instead, try this:
var test:String;
function setVar():void{
this.test = 'foorBar';
}
trace(test); //output: null (undeclared)
setVar();
trace(this.test); // output: fooBar
Looking at this, I did two things:
I moved the declaration of test into global space, meaning any object in that object will have access to it
I renamed SetVar to setVar. This has nothing to do with your question, but in AS3, the standard naming conventions dictate you use lowerCaseCamelCase for all objects (including functions), UpperCaseCamelCase for all Class names, and lowercasename for all package names. Again, unrelated but it is good to learn.
Now, ideally, you would probably want to do that setVar function slightly differently. To allow for better abstraction (basically making your code as generic an reusable as possible), you would want to return the value from the function rather than manually set the variable in the function.
var test:String;
var anotherTest:String;
function setVar():String {
return 'foorBar';
}
this.text = setVar();
this.anotherTest = setVar();
trace(this.test); // output: fooBar
trace(this.anotherTest); // output: fooBar
So that allows you to use that function with any String variable imaginable. Obviously, that is not very useful here since it doesn't do any logic. But I am sure you can see how that could be expanded with more code to make it more dynamic and much more useful
EDIT: As an afterthought, I used the this keyword. In AS3 (and a few other languages), this refers to the scope of the current class (or current frame, in case of timeline frame coding). So this.test refers to a variable test declared in the scope of the frame or class.
I am not entirely sure what you are looking for because there is no code associated with your question. However I will impart a bit of information I feel relates to the subject.
if you declare your variables in the class then you can reference them from a function as such:
package{
import flash.display.MovieClip;
public class DocumentClass extends MovieClip{
public var example:String = 'dog';
public function DocumentClass(){
trace(example); // dog
testFctn();
trace(example); // frog
}
public function testFctn(){
example = 'frog'
}
}
}
if you want to reference the variable of a parent class this.parent['variableName'] can be useful too. or a sibling of your working class sharing a parent class, this.parent['childClass']['variableName'] ...
Since you are declaring the variable within the function, its scope is restricted to that function only.
Try declaring the variable outside the function and initializing it in the function instead.
You should then be able to access it from root.
But if you wish to declare a variable on root from within a function (highly unusual requirement) then you can try doing:
document["variableName'] = value;
or
root["variableName'] = value;
inside the function.

How do I pass reference of an initialised object from one class to another class, to add (addChild) to display list?

I created this simple example
because I was using a more complex class, a menu item that I wanted to initialise all the settings in the Main class and then add it in in the Game class (and updating it) when needed (both classes are separate)
Class: Main (document class, is (ideally) where everything is initialised/created)
public class Main extends MovieClip
{
//testing passing on reference to Game
private var testBitmap:Bitmap;
private var testBitmapData:BitmapData;
private var testArray:Array;
public function Main():void
{
testBitmapData = new BitmapData(256, 256, false, 0xCCDDEE);
testBitmap = new Bitmap(testBitmapData);
testArray = [];
testArray.push(testBitmap); //Array for reference
game = new Game(540, 960, testArray);
//create Game class, pass reference to Object
game.x = 0;
game.y = 0;
}
}
Class: Game (created by document class, is (ideally) where everything runs)
public class Game extends MovieClip
{
private var testingArray:Array
public function Game(gameWidth:int, gameHeight:int, testArray:Array):void
{
this.testingArray = testArray; //assign to local Array and access
addChild(testingArray[0]);
//addChild to Game from an item intialised in Main, doesn't work >___<
}
}
.
.
.
the thing is, in my original Game class; it receives an initial bundle of cached BitmapData and a list Array that tells it which BitmapData it needs to cycle through
cut-down here (and that reference only for updating works (if I addedChild in Main already):
public function Game(gameWidth:int, gameHeight:int, cachedBitmapClipArray:Array)
{
this.cachedBitmapClipArray = cachedBitmapClipArray;
private function update():void
{
for each (tempCachedBitmapClip in cachedBitmapClipArray)
{
tempCachedBitmapClip.updateCurrentTile();
//but updating through the reference to an item initialised in Main works !!
}
}
}
.
how do I make the reference and passed in objects (or have access to) behave as in the original instance ?? (being able to addChild)
i.e. can objects cross 'scopes' (??) or should objects only be controlled (instantiated) in the class where they have been initialised
Well to answer the last question, yes objects can be passed from one object to another. I'm having a hard time understanding what exactly the problem is though. In generic programming terms Object or any other complex type are passed by reference, primitive types are also passed by reference but the internals of the AVM handle them in such a way as to treat them as passed by value. For a pretty clear (in my eyes at least) explanation of how arguments are passed via function/method calls, read here:
http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f56.html
Check out other areas in the tree navigation on the left for more details on the language itself.
One thing to note though I think the compiler will work around this error, in the first bit of code you posted Game has a return value of :void for a constructor there should be no declared return type since it should be implicitly typed as whatever the class is.
To add some explanation in my own words regarding pass by reference or pass by value. Pass by value means that the value of the object, that is the bytes stored at the location pointed to by the variable are actually copied to a new memory location which is then used. What this means in practice is they are not the same bytes of memory but rather they are two separate memory locations each with the same value stored in them, modification of one value after the passing doesn't affect the original. Pass by reference is to say you pass the memory location therefore no new memory for the value is allocated, both values are actually pointing to the same memory location (the pointer to them itself may be different but that pointers both point to the same location in memory). In this case the objects are the same.
What you're doing is advisable, dividing the labor and enapsulating particular types of functionality in classes does make the code easier to work with. I think the only problem would be if the Game object itself is never added as a child to something in the display tree (something that is attached to the stage). It appears you're creating a new Game and setting it's position but never adding it as a child to the stage, then adding anything to Game is never a part of the display tree.

AS3 - Parametrized Factory method using actual class name

Rather than use a hard-coded switch statement where you pass it the string name of a class and it then instantiates the appropriate class, I'd like to pass the actual name of the class to my factory method and have it dynamically create an instance of that class. I thought it would be trivial and am surprised it is not working. I must be missing something quite basic:
sample code:
createProduct(50, "Product1Class");
createProduct(5, "Product2Class");
private function createProduct(amount:uint, productClassName:String):void {
var productReference:Class;
try {
productReference = getDefinitionByName(productClassName) as Class;
for (var i:uint = 0; i < amount; i++) {
var product = new productReference() as ProductBaseClass; // throws reference error!
}
} catch (error:ReferenceError) {
throw new ReferenceError(error.message + " Have you linked a library item to this class?");
}
}
The only thing that may be a little odd (not sure) is that these "products" are actually linked Library items (ie: I have a movieClip in the Library that has a linkage to Product1Class and another to Product2Class both of which extend ProductBaseClass, which in turn extends MovieClip.
Why the ReferenceError?
If you have a runtime loaded library then the Class's are not compiled into the main swf, so you get the runtime reference error when you try to create them.
To work around this you can declare "dummy" vars of the classes you want to compile, or if using the flex compiler there are options to include the classes you are missing.
e.g. declare these anywhere in your project
private var p1:Product1Class;
private var p2:Product2Class;
Its a frustrating problem, if your classes extend MovieClip which is a dynamic class you might be able to access the properties etc by doing something like this:
var product:MovieClip = new productReference() as MovieClip;
p1["someCustomProperty"]; //Dot notation might work here as it is a dynamic class
Chris is absolutely right, the ReferenceError is actually being thrown during the call to getDefinitionByName, meaning that the reflection method cannot find Product1Class or Product2Class in your application domain. You can always check if a definition is available by checking the application domain directly, like:
// inside your createProduct method, yields 'false'.
ApplicationDomain.currentDomain.hasDefinition( productClassName );
Are these library assets loaded in at runtime? If so, you can either make sure that the library swf is loaded into the current application domain by adding an appropriately configured LoaderContext to your Loader, or you can replace the call to getDefinitionByName with the loaded swf's application domain's getDefinition method.
getDefinitionByName() and ApplicationDomain.currentDomain.hasDefinition() require full qualified class names. The example code in the original post works when Product1Class and Product2Class are in the default package. However, if you move the product classes to another package, you have to make sure that you are supplying the fully qualified class name to getDefinitionByName().
So if we put our product classes in com.example.products, then the call becomes:
productReference = getDefinitionByName("com.example.products.Product1Class") as Class;
I'm not really sure what the best practice is with this kind of dynamic factory class, but what I ended up doing (since all products were in the same package) was to create a constant within my factory class that defines the package for my products:
private const PRODUCT_PACKAGE:String = "com.example.products."; // note the trailing period
So that way your client code doesn't need to know (nor define) the product package. You just prepend this constant to your product class name when using getDefinitionByName().