2009年3月4日 星期三

Memory Leak While Using Image

It is so strange that I always got a lot of memory leaks when using a plenty of Image object. The following are common usage from me or others:

var imgDummy = new Image();
imgDummy.source = "whatever.png"
this.addChild(imgDummy);

After the above codes, an image is created and put in the container.

while removing image from the container, we always use:

this.removeChild(imgDummy);

But it leaks.

This is so strange. While using the same scenario in other programming language like Java, C#, this problem won't be there. But it is in Flex.

So, I create a project for testing this symptom. I load an image 300 times and put them in a container:

private function createObjects():void

{

var imgTest:Image;

for(var i:int=0;i<300;i++)

{

imgTest = new Image();

imgTest.source = "loading.png";

m_Container.addChild(imgTest);

}

imgTest = null;

}


where m_Container is a Canvas object.

As to freeing code, I wrote three kind:
1. free image by removeAllChildren

private function freeObjects1():void

{

m_Container.removeAllChildren();

}


2. free image by removeChild one by one

private function freeObjects2():void

{

var imgTest:Image;

while(m_Container.getChildren().length > 0)

{

imgTest = m_Container.getChildAt(0) as Image;

m_Container.removeChild(imgTest);

}

imgTest = null;

}


3. free image by removeChild and set the source of image to empty string

private function freeObjects3():void

{

var imgTest:Image;

while(m_Container.getChildren().length > 0)

{

imgTest = m_Container.getChildAt(0) as Image;

imgTest.source = "";

m_Container.removeChild(imgTest);

}

imgTest = null;

}

4. free image by removeAllChild() and set the source of image to empty string

private function freeObjects4():void

{

var imgTest:Image;

for(var i:int;i

{

imgTest = m_Container.getChildAt(i) as Image;

imgTest.source = "";

}

imgTest = null;

m_Container.removeAllChildren();

}


This is the profiling image of each freeing method:
Method 1:


This is a strange image that the first and second "V style" sharp occurred while creating images after free them. But after these two "V style" sharp, it doesn't free any memory.

Method 2:

Basically, I press for times create and free, the memory raises a little. In this method, Flex recycles the created objects, but it doesn't free any memory while freeing them.

Method 3:
This method works. The key point is imgTest.source = "". This expression makes the SWFLoader inside Image unload everything.


Method 4


This method works, too. There is no difference between method 3 at memory freeing.

The sample program is here.

Be careful, I the profiling is running at "debug" mode. But app always running at release mode. The above methods seems useless at release mode. This may be caused by System.gc() is only workable at debug mode and garbage collection is done automatically by Flash VM at release mode. So, there is no way to force gc at release mode. 

I had googled this issue for a few days, it seems there is no solution for this problem. But at least, in the debug mode, we can make sure the memory doesn't leak.  


some references about memory leaks:

2008年9月2日 星期二

Error #2044: 未處理的 IOErrorEvent.. Error #2036

福克斯最近接到朋友的通知,發現福克斯之前寫的程式出現了『未處理的 IOErrorEvent』。天啊,這個問題在網路上也有超多人遇到的。

這個問題的主要來源是,呼叫 Loader.load 的時候,並沒有聽取 IOErrorEvent 的事件。當然,很多人都會說,沒有啊!我的都有聽。可是,每次到最後,證實出來都是沒有聽取。

當然,沒有聽取 IOErrorEvent 事件並不是整件事的原罪。會發生這個問題,最主要的原因有:1. 檔案找不到、2. 檔案壞掉、3. 其它原因導致檔案無法讀取。

所以啦,下次大家看到這個問題的時候,可以檢查是不是所有的 loader 都有聽取 IOErrorEvent 的事件。檢查完之後,別忘了,一定是程式出錯了,不然,不可能 Flash 會說『未處理的 IOErrorEvent』。

不過,話說回來,福克斯其實在 Flex 2 的時候,就已經發生過了。所以,福克斯一向養成聽取這個事件的好習慣。但是,為什麼福克斯的程式還是會有這個問題。最主要的原因是,福克斯使用了一個別人寫的 Flex 2 元件。然而,福克斯之前就以為,如果給它一個錯誤的網址,它能夠處理,或是 throw 一個 Error 出來。但是,實際上,並沒有。

ps 有一個很重要的事, Loader 這個類別是沒辦法直接聽取 IOErrorEvent,而是要用 Loader.contentLoader.addEventListener(...) 的方式。

2008年8月19日 星期二

Binding 的使用, part 1

Binding 是 Flex 中一個很特別的項目。許多傳統的程式語言中,大多都沒有類似的東西。說真的,福克斯認為,要設計一個非常易於使用的 Binding 並不是件容易的事。對福克斯來說,Flex 的 Binding 算是一個還算簡單,但還不夠直覺的方式。不過,經過幾次的使用,福克斯覺得 Binding 真的是一個很方便的工具。

最近,福克斯須要教同事,如何設計 Flex。當我們討論到 Binding 的時候,福克斯發現,這個觀念似乎對某些程式設計師來說,不容易被接受。一些 Flex 的設計師們可能會覺得,這麼好用的東西,怎麼可能會不容易接受。福克斯發現,不易被接受的原因並不是不好用,而是不夠直覺。說真的,Flex Binding 的設計可以算是目前不錯的設計。所以這一系列的文章,將會和大家一起討論,如何正確地使用 Binding 這項工具。

Binding 是指一個永遠成立的方程式,例如: objA.x = objB.y + n。此時,如果 objB.y 改變的時候,objA.x 也會跟著被修改。針對這種關係,在傳統的程式語言中(如 Java, C#, C++, JavaScript…等),我們必須要在 objB 中加入一個 event listener 用來聽取 y 修改的事件,進而重新計算方程式的結果。

Flex 的 Binding 也是使用同樣的設計方式。當我們在 Flex 中建立一個 Binding 時,Flex 會自動建立 event listener 並自動建立重新計算方程式的 function。

福克斯建議,第一次接觸 Binding 最好從最簡單的模式開始學習。所以,以下是完成 objA.x = objB.y 的 Binding 呼叫方式:

BindingUtils.bindProperty(objA, "x", objB, "y");

這段程式中,BindingUtils 是 mx.binding.utils package 中用來建立 Binding 的物件。最簡單使用 Binding 的方式,就是讓兩個物件的一個屬性互相連結在一起。以上面的這個例子來說,我們使用最簡單的方式,即 bindProperty 來連結兩個物件的屬性。我們可以直接以方程式 objA.x = objB.y 的方式來看,呼叫 BindingUtils.bindProperty 時,可以使用同樣的順序來呼叫。所以,我們可以直接將方程式的參數,直接代入 BindingUtils.bindProperty 之中。要特別注意的地方是,物件的 property 部份,都必須要以『字串』的方式輸入。




In Flex, the binding is made by BindingUtils and ChangeWatcher.

2008年8月8日 星期五

Autoresizing Canvas


Auto-resizing is a common feature in contemporary programming languages, but not Flex. It is uncommon. After surveying, I have to admit that I can't find it. So, I decide to write it by my own. 

The auto-resizing is based on the mouse position and dragging operation. The dragging operation can be observed by mouse down or drag start.  The mouse position can be observed by the mouse move event. But we won't be notified while the mouse is moved out side of canvas. Another way to be aware of mouse can be done by reading mouseX and mouseY in enter frame event, even if the mouse is moved out of canvas. 

The example program will be designed as:
1. using a boolean flag to record mouse is down
2. resizing the inner canvas while the mouse is dragging the right-bottom corner
3. check if auto-resizing while entering frame

The following are the code:

<mx:canvas id="outer"  enterFrame="handleEnterFrame(outer.mouseX, outer.mouseY)" mouseUp="handleMouseUp(inner.mouseX, inner.mouseY)" mouseDown="handleMouseDown(inner.mouseX, inner.mouseY)" mouseMove="handleMouseMove(inner.mouseX, inner.mouseY)" x="114" y="56" width="334" height="271" borderStyle="solid" borderColor="#FFFFFF" borderThickness="5">

<mx:canvas id="expander" x="0" y="0" width="{inner.width+ 20}" height="{inner.height + 20}">

<mx:canvas id="inner" x="10" y="10" width="200" height="200" borderStyle="solid" borderColor="#33444F" borderThickness="2">

</mx:canvas>

</mx:canvas>

</mx:canvas>


The above codes declare three canvases, outer, expander, and inner. The outer is the bound while holds the resizing canvas. The expander is one of the resized canvas who preserves 30 pixels offset between inner and outer.  The inner canvas is the one we want to resize.

private function handleMouseDown(x:Number, y:Number):void

{

if(x > (inner.width - 20) && y > (inner.height - 20))

{

m_MouseDown = true;

m_MouseOffsetX = x;

m_MouseOffsetY = y;

}

}


These codes handles mouse down event of  outer canvas, but the x and y arguments are mouseX and mouseY of inner canvas. This method marks mouse down while a user presses right-bottom corner.

private function handleMouseUp(x:Number, y:Number):void

{

m_MouseOffsetX = 0;

m_MouseOffsetY = 0;

m_MouseDown = false;

}


These codes handles mouse up event of outer canvas. It marks mouse not down.

private function handleEnterFrame(x:Number, y:Number):void

{

if(m_MouseDown){

if(x > outer.width - 30 && x <>

{

inner.width += 1;

m_MouseOffsetX = inner.mouseX;

outer.horizontalScrollPosition+=2;

trace("Frame Increase X");

}

if(y > outer.height - 30 && y <>

{

inner.height += 1;

m_MouseOffsetY = inner.mouseY;

outer.verticalScrollPosition+=2;

trace("Frame Increase Y");

}

}

}


These codes actually resizing inner canvas. While user presses the right-bottom corner of inner canvas and moves the mouse to the offset between inner canvas and outer canvas, it auto-resizes. This method increase width or height of inner canvas by one and change the scroll bar position to the end.

The following is the view of the above code:


















 

2008年8月4日 星期一

Rotation in Flex

Rotation is a easy task in Flex. But it's not easy while developing an integrated editing environment. Recently, I was starting to write an editor for photos. I never learned graphics techniques. So, I don't know the vector graphics.

At the first, I found an equation to calculate a rotated point, which is x = dx + x*cos(a) - y*sin(a) and y = dy + x*sin(a) + y*cos(a). That's great. I use it anywhere to calculate a non-rotated point to rotated point. When calculating a rotated point to non-rotated point, I was going to find an equation too, but nothing found. At that time, I felt some thing wrong. Why I could find an equation but not its reverse equation. So, I open my only one book about graphics, Filthy Rich Client. I found I was totally wrong.

Rotation is always done by matrix of affine transform. We can find the equation I found before is the result of applying a rotated and shifted matrix. So, it doesn't need to use math equation to calculate all the time. All you need is just call: transformPoint. If you want to reverse it, just call invese() and then you can have a reversed matrix. It's simple and easy. 

Transform non-rotated to rotated:

var oMx:Matrix = new Matrix();

oMx.rotate(radian);

oMx.translate(dx, dy);

return oMx.transformPoint(new Point(x, y));


The dx and dy is the about point. The x and y is the relative point to the about point. The result of this method is a absolute point.

Transform rotated to non-rotated:

var oMx:Matrix = new Matrix();

oMx.rotate(radian);

oMx.translate(dx, dy);

oMx.invert();

return oMx.transformPoint(new Point(x, y));


The dx and dy is the about point. The x and y is the absolute point. The result of this method is the relative point to the about point.

The following is a sample applying the above functions (two points in the rect are the rotating point.):