Garbage collection reference variable within a Vector - actionscript-3

I am implementing garbage collection within an AS3 app. In one part, several display objects are created within a loop like so:
for(var i:uint = 0; i <= this._exampleVector.length - 1; i++)
{
this._customText = new CustomTextObject(this._exampleVector[i].playlistText), this._customTextWidth);
this.addChild(this._customText);
etc etc
this._customTextVector.push(this._customText); // used for ref in garbage collection
}
I then perform my garbage collection preparation by looping through the _customTextVector variable.
for(var i:uint = 0; i <= this._customTextVector.length - 1; i++)
{
this.removeChild(this._customTextVector[i]);
this._customTextVector[i].gcAllObjects();
**this._customTextVector[i] = null;**
}
When I try to make the _customText within the _customTextVector null, this does not work. It only makes the index inside the Vector null. Any ideas on how to do this or another method to garbage collect?
Thanks
Chris

Is it possible to do following after looping through all the indices
_customTextVector =null;

In order to cause the AS3 Garbage Collector to GC your objects, you need to remove all references to them (including event listeners). On the next GC pass, the object's memory will be freed. There is no way to directly, instantly "null" an object like you want.
If you're having issues with memory, have a look at this post.

Related

Under what circumstances is a reverse for loop faster in Flash and why?

According to Adobe, a reverse for loop is the fastest way to do a loop:
http://www.adobe.com/devnet/flash/articles/optimizing-flash-performance.html
I could not create a test where a reverse for loop was consistently faster than a normal loop, but I'm assuming Adobe knows Flash. Does anyone know under what circumstance this is true and why?
You don't have to recalculate the length of an array or Vector if you iterate backwards.
for(var i:int = list.length; i > 0; i--)
// -------------^^^^^^^^^^^ Length is calculated once for the start value.
Versus:
for(var i:int = 0; i < list.length; i++)
// --------------------^^^^^^^^^^^ Length is calculated for each iteration.
FYI, the difference is speed is negligible. I personally use whichever is more readable to me (the former).
That aside - if you are iterating over a collection where all objects are of the same type, you should be using a for each loop. This is much faster, more readable and more logical than either of the above. The reason that it is faster is because no type conversion is required at each iteration - the type is set when you define the loop:
var strings:Array = ["a","b","c"];
for each(var i:String in strings)
{
trace(i);
}
Marty is correct: if you use a reverse for loop you have to get the length value only once. However, this can be easily achieved through a normal for loop, and assigning the length to a variable beforehand.
A further optimized reverse for loop would look like:
for(var i:int = list.length; i--;)
which results in looping through every element in the array in reverse without a third for loop argument.

adding items from an array multiple times to the stage

I have a for loop which passes 11 times:
private var currentItem:uint;
for(var i:uint = 0;i<10;i+){
addChild(arr[currentItem]);
currentItem++;
if(currentItem == arr.length){
currentItem = 0;
}
}
So the problem is that the array only contains 6 items. So when it comes to the 6th item the currentItem resets and the next 4 items that are getting added are the 4 first from the array again. Now when I trace the items, the last 4 trace "null". My question is, how can I add items from the array multiple times without losing its properties etc?
There's nothing inherently wrong with your loop. However, a DisplayObject can only be on the display list once. It can't have multiple parents or be a child of the same parent many times. That's why your code isn't working.
Update:
If you want to create new instances from a list of Classes you can do that, but your current approach wont work. This is what you need to do:
// the square bracket notation is shorthand for creating an array.
// fill the array with references to *classes* not instances
var classes:Array = [ MyClassOne, MyClassTwo, MyClassThree ];
// we run the loop much as you did, but we can make it much more compact
// by using the modulus operator
// since the array is full of classes, we can use the new operator to
// create new instances of those classes and add them to the display-list
for(var i:uint = 0; i < 10; i++ ){
addChild(new classes[i % classes.length]);
}

For-loop variable scope confusion

I have noticed a weird behavior of the variables in for loops. It's not really a problem, but it disturbs me a lot.
Actually I've created two loops this way:
for (var i:uint; i<19; i++) SomeFunction (i);
for (var i:uint; i<26; i++) SomeOtherFunction (i);
What I received was a compilation warning:
Warning: Duplicate variable definition.
This warning really surprised me. Nothing like that ever happened to me in other languages.
It seems that the i variable gets into the scope that is higher in the hierarchy and becomes available out of the loop's block. I've also tried to embrace the loop block in a curly brace, but it didn't change anything.
Why does it happen? Is it normal? Is it possible to avoid it? For now I've just set different names for both of the variables, but that's not a real solution I think. I'd really like to use the i-named variable in most of my for-loops.
yes, the loop increment variable is in the scope of the loops parent, not inside the loop itself. This is intentional, for examples like this:
public function getPositionOfValue ( value:String ) : int
{
for ( var i:int = 0; i < someArray; i++ )
{
if (someArray[i] == value )
{
break;
}
}
return i;
}
this allows you to access the value of i once the loop is over. There are lots of cases where this is very useful.
What you should do in the cases where you have multiple loops inside the same scope is var the i outside of the loops:
public function getPositionOfValue ( value:String ) : int
{
var i:int;
for ( i = 0; i < 15; i++ )
{
//do something
}
for ( i = 0; i < 29; i++ )
{
//do something else
}
return i;
}
then you get rid of your warning. The other thing to consider is to name your loop increment variables something more descriptive.
Update: Two other things to consider:
1) you shouldn't use uints except for things like colors and places where Flex expects a uint. They are slower than int's to use. Source]1 Update: it looks like this may no longer be the case in newer versions of the flash player: source
2) when you var a loop increment variable inside of a loop declaration, you want to make sure you set it to the proper initialization value, usually 0. You can get some hard to track down bugs if you dont.
As mentioned here, as3 has global and local scope and that's about it.
It does not do block-level scoping (or for-level either). With hoisting, you can even write to variables before you define them. That's the bit that would do my head in :-)
Early versions of Visual C had this bug, leading to all sorts of wonderful funky macro workarounds but this is not a bug in as3, it's working as designed. You can either restrict your code to having the declaration in the first for only or move the declaration outside all the for statements.
Either way, it's a matter of accepting that the language works one way, even though you may think that's a bad way :-)
Declare the variable i outside the loops to avoid this. As long as you reset it (i=0) you can still use it in all loops.
var i : uint;
for (i=0; i<19; i++) SomeFunction(i);
for (i=0; i<26; i++) SomeOtherFunction(i);

Segfault Copy Constructor

My code is as follows:
void Scene::copy(Scene const & source)
{
maxnum=source.maxnum;
imagelist = new Image*[maxnum];
for(int i=0; i<maxnum; i++)
{
if(source.imagelist[i] != NULL)
{
imagelist[i] = new Image;
imagelist[i]->xcoord = source.imagelist[i]->xcoord;
imagelist[i]->ycoord = source.imagelist[i]->ycoord;
(*imagelist[i])=(*source.imagelist[i]);
}
else
{
imagelist[i] = NULL;
}
}
}
A little background: The Scene class has a private int called maxnum and an dynamically allocated Array of Image pointers upon construction. These pointers point to images. The copy constructor attempts to make a deep copy of all of the images in the array. Somehow I'm getting a Segfault, but I don't see how I would be accessing an array out of bounds.
Anyone see something wrong?
I'm new to C++, so its probably something obvious.
Thanks,
I would suggest that maxnum (and maybe imagelist) become a private data member and implement const getMaxnum() and setMaxnum() methods. But I doubt that is the cause of any segfault the way you described this.
I would try removing that const before your reference and implement const public methods to extract data. It probably compiles since it is just a reference. Also, I would try switching to a pointer instead of pass by reference.
Alternatively, you can create a separate Scene class object and pass the Image type data as an array pointer. And I don't think you can declare Image *imagelist[value];.
void Scene::copy(Image *sourceimagelist, int sourcemaxnum) {
maxnum=sourcemaxnum;
imagelist=new Image[maxnum];
//...
imagelist[i].xcoord = sourceimagelist[i].xcoord;
imagelist[i].ycoord = sourceimagelist[i].ycoord;
//...
}
//...
Scene a,b;
//...
b.Copy(a.imagelist,a.maxnum);
If the source Image had maxnum set higher than the actual number of items in its imagelist, then the loop would run past the end of the source.imagelist array. Maybe maxnum is getting initialized to the value one while the array starts out empty (or maxnum might not be getting initalized at all), or maybe if you have a Scene::remove_image() function, it might have removed an imagelist entry without decrementing maxnum. I'd suggest using an std::vector rather than a raw array. The vector will keep track of its own size, so your for loop would be:
for(int i=0; i<source.imagelist.size(); i++)
and it would only access as many items as the source vector held. Another possible explanation for the crash is that one of your pointers in source.imagelist belongs to an Image that was deleted, but the pointer was never set to NULL and is now a dangling pointer.
delete source.imagelist[4];
...
... // If source.imagelist[4] wasn't set to NULL or removed from the array,
... // then we'll have trouble later.
...
for(int i=0; i<maxnum; i++)
{
if (source.imagelist[i] != NULL) // This evaluates to true even when i == 4
{
// When i == 4, we're reading the xcoord member from an Image
// object that no longer exists.
imagelist[i]->xcoord = source.imagelist[i]->xcoord;
That last line will access memory that it shouldn't. Maybe the object still happens to exist in memory because it hasn't gotten overwritten yet, or maybe it has been overwritten and you'll retrieve an invalid xcoord value. If you're lucky, though, then your program will simply crash. If you're dealing directly with new and delete, make sure that you set a pointer to NULL after you delete it so that you don't have a dangling pointer. That doesn't prevent this problem if you're holding a copy of the pointer somewhere, though, in which case the second copy isn't going to get set to NULL when you delete-and-NULL the first copy. If you later try to access the second copy of the pointer, you'll have no way of knowing that it's no longer pointing to a valid object.
It's much safer to use a smart pointer class and let that deal with memory management for you. There's a smart pointer in the standard C++ library called std::auto_ptr, but it has strange semantics and can't be used in C++ containers, such as std::vector. If you have the Boost libraries installed, though, then I'd suggest replacing your raw pointers with a boost::shared_ptr.

as3 referencing members of an array not explicitly defined

I am creating a bunch of objects in an array. I'm used to doing this iteratively, like
for(i:int; i < number; i++){
ball= new Ball;
balls.push(ball);
}
and then later I can make reference to balls[i]. Now however I'm creating the objects with mouse click, not with a for loop, so I don't have that [i], so my other code makes reference to just "ball", which means it affects only whichever one was just created. Is there any reasonable way to 'name' each object arbitrarily so I can later say "each of you go off and do your own thing, and ignore everyone else"?
There is a couple of solutions,
If you have a reference to the ball, you can use
myArray.indexOf(myBall)
Which will give you its position in the array, so you can talk to it.
you can also store them with a name value like this:
myArray["name" + uniqueIdentifier] = myBall;
That would replace your push statements.
Alternatively you could just loop over them all like this:
for(var i:int = 0; i < myArray.length; i++)
{
ball = myArray[i];
//perhaps you stored a value on ball to distinguish when it was created
if(ball.wasCreatedByMouseClick) // do something
}
Hope that gives you some ideas.
i don't know what your code is doing but according to what you described i'd use a dictionary to store your objects with a string key.
dictionary class behaves as an object in which you can set a dynamic property (key) and its value (your object).
it's different from a object because keys could be anything (strings, objects, arrays,...anything).
this is an example:
var d:Dictionary = new Dictionary (true);
d["myBall"] = new Ball();
function getBallByKey(key:String):Ball
{
return d[key];
}
depending on the scenario a good thing is to set dictionary references to weak. this is to avoid memory leaks (actually letting the object "die" as no other reference but the dictionary itself is pointing at that object)