'분류 전체보기'에 해당되는 글 2491건

  1. 2014.08.20 [펌] Flash 애플리케이션의 속도와 메모리 개선 : Object Pool의 활용
  2. 2014.08.20 [펌] Flash Player 의 가비지 컬렉션(GC) 동작 방식에 대해
  3. 2014.08.20 [펌] Flex Builder에서 디버깅&프로파일링 하기
  4. 2014.08.20 [펌] Flex Builder에서 디버깅&프로파일링 하기 -1부-
  5. 2014.08.20 Understanding and Solving Internet Explorer Leak Patterns
  6. 2014.08.20 [FLEX] Memory leak 문제
  7. 2014.08.06 actionsript 데이터 유형 설명
  8. 2014.07.30 Introducción a Apache FlexJS
  9. 2014.07.29 [펌] Oracle JDBC의 CachedRowSet Implement Bug
  10. 2014.07.25 [펌] flex StringUtil.substitute 사용하기
  11. 2014.07.23 [펌] [Oracle] MySQL의 Limit처럼 사용해보자.
  12. 2014.07.23 [펌] DBMS별 지원되는 최대 컬럼수(Oracle,ms-sql,mysql,db2)
  13. 2014.07.23 [펌] Spring에서 SQL Eception 처리
  14. 2014.06.25 flex charts link
  15. 2014.06.24 FlexLib component list
  16. 2014.06.20 The Blogagic Flex Explorer Collection
  17. 2014.06.19 [펌] [Flex3] Error : mx.managers.IDragManager(으)로 변환할 수 없습니다 ...
  18. 2014.06.17 [펌] Flash 애플리케이션의 속도와 메모리 개선 : Object Pool의 활용
  19. 2014.06.13 [펌] [Flex]TextInput 글자제한 ( restrict )
  20. 2014.06.12 [펌] DataGrid CheckBox 스크롤때 없어지는문제 해결 [출처] DataGrid CheckBox 스크롤때 없어지는문제 해결|작성자 neodijkstra
  21. 2014.06.12 [펌] 데이터그리드 스크롤 버그
  22. 2014.06.12 [펌] Flex DataGrid ItemRenderer 스크롤 버그
  23. 2014.06.10 [펌]플렉스 TextArea ScrollBar 자동갱신
  24. 2014.06.10 [펌] FLEX 마우스 커서 모양 바꾸기 (시스템)
  25. 2014.06.10 [펌] flex 컴포넌트 크기 변경하기
  26. 2014.05.30 [펌] 이클립스 Indigo SVN 설치
  27. 2014.05.21 [펌] jqGrid 에서 postData 처리시 주의 사항 (paging,search,reload) 1
  28. 2014.04.30 JSON Editor
  29. 2014.04.25 [펌] [무설치] 휴대용 위키 도쿠위키 DokuWiki Stick
  30. 2014.04.23 [eclipse]org.springframework.beans.factory.BeanCre Error creating bean with name 'exceptionTransfer'
카테고리 없음2014. 8. 20. 14:37
반응형

Flash 애플리케이션을 제작할때 항상 고민되는 것은 메모리와 속도일 것이다. 이 문서는 이러한 고민을 했던 분들을 위해 본인의 경험을 반영하여 작성한 것이다. 주의할 것은 이 문서에서 다루는 Object Pool은 메모리, 속도 개선을 위한 하나의 방법일 뿐이지 전부는 아니다라는 것이다. Object Pool이 어떻게 당신의 애플리케이션의 속도와 메모리를 개선시켜줄 것인가 전달하는 것이 문서의 목표이다.

목차 

  1. 객체 관리에 대한 질문!
  2. new 연산자는 비싸다!
  3. Object Pool이란?
  4. Object Pool을 도입할 때 고려사항
  5. Object Pool을 사용해보자.
  6. Object Pool과 new 연산자 사용 비교
  7. 메모리 사용의 폭이 급격한 애플리케이션은 좋지 못하다.
  8. 재사용할 수 있는 클래스 제작
  9. 정리하며
  10. 참고글



1. 객체 관리에 대한 질문!
제작된 애플리케이션에 필요한 최소한의 메모리에 대해서 생각해 본적이 있는가? 하나의 애플리케이션에 들어가는 최소 자원(resource)은 얼마나 될까? 그 자원은 얼마나 자주 사용하고 버리는 것일까? 

슈팅 게임을 보면 동일한 적군전투기가 많이 나온다. 그 중에 적군전투기1이 있다고 하자. 적군전투기1이 한 화면에 5개가 나왔다. 아군전투기에 의해 죽든 화면에서 사라지든 어쨌든지 언젠간 화면에서 사라지고 쓸모없게 된다. 좀 있다가 또 적군전투기1이 3개가 나온다. 또 반복해서 사라진다. 당신이 이 슈팅 게임을 만든다면 적군전투기1 관리를 어떻게 할 것인가? 즉, 화면에 나타날때 new로 생성하고 사라지면 delete로 메모리에게 해지요청할 것인가? 아니면 어느 특정 영역에 화면에 나타날 전투기1을 미리 5개에서 10개 생성해놓고 필요할 때마다 재사용할 것인가? 

2. new 연산자는 비싸다!
new는 클래스를 통해 객체를 생성하는 연산자이다. 이는 비교적 비용이 비싼편이다. 비용이 비싸다는 말을 구체적으로 설명하자면 new를 통해 객체를 생성하여 메모리에 올리고 다 사용했다고 판단될때 delete이나 null등을 이용해 객체참조를 제거하여 가비지 컬렉터에 맡기게 되며 결과적으로 가비지 컬렉터는 이 객체를 어느 특정시점에 제거하는 일련의 객체 생성과 삭제까지 동작이 꽤 복잡하여 메모리, CPU연산등의 자원소비가 크다라는 것을 의미한다. 특히나 클래스의 크기가 크고 복잡하다면 그 비용은 더욱 갑절로 들게 된다. new 연산 사용은 이처럼 비싼 비용을 지불해야 하기 때문에 할 수 있다면 최소한으로 사용하도록 하는 것이 속도와 메모리 관리에 도움을 준다.

앞서 설명한 슈팅 게임을 보자면 적군전투기1은 수시로 보였다가 안보였다가 한다. 이 경우 new, delete를 반복한다면 경우에 따라서 엄청난(?) 비싼 비용을 지불하게 된다. 

다른 예를 들어보겠다. 본인은 얼마전에 스타플(http://starpl.com)이라는 사이트에서 제공하는 타임라인 애플리케이션을 Flash로 개발했다. 타임라인은 사용자의 기록, 앨범등을 시간순으로 보여주게 되며 좌우측으로 이동해서 내 인생의 기록을 보는듯한 느낌을 주도록 만들었다. 이때 화면에 보여지는 사용자의 기록들이 수시로 보여졌다가 사라진다. 이때 사용된 기록을 보여주는 시각객체(DisplayObject)를 new와 delete를 반복해서 생성 및 삭제한다면 너무 비싼 비용을 들이는 것과 같다. 

스타플의 타임라인

참고글 : 스타플 타임라인 업그레이드 및 앨범 기능 추가 소식

앞의 2가지 예에서 언급했듯이 자주 보여지고 사라지는 객체를 필요할 때마다 new연산자를 사용하는 것보다는 어느 특정 영역에 미리 생성해놓고 재사용을 반복하는 구조를 도입한다면 메모리와 CPU연산에 있어서 큰 비용절감을 기대할 수 있게 된다. 

3. Object Pool이란?
Object Pool(객체 풀)은 객체를 미리 만들어놓고 담아놓는 메모리 영역이다. 개발자는 이 Object Pool에 미리 만들어진 객체를 사용하고 다 사용한 뒤에는 다시 반환시킴으로서 재사용(또는 대여)을 하도록 한다. Object Pool을 사용하면 처음 객체를 생성할때와 더 필요한 객체가 있는 경우를 제외하고 new 연산을 최소화 할 수 있다. 

4. Object Pool을 도입할 때 고려사항 
Object Pool은 미리 객체를 생성하고 지속적으로 재사용하기 위해 사용한다. 이때 미리 객체를 생성한다는 것은 초반 애플리케이션 동작 퍼포먼스에 영향을 미칠 수 있다. 

Object Pool을 사용했을때 다음과 같은 장점을 얻을 수 있다.

1. new 연산자를 자주 사용해야하는 경우에 도입하면 애플리케이션 속도 향상을 기대할 수 있다. 
2. 가비지 컬렉터에 의존하지 않는 메모리 관리를 꾀할 수 있다.

하지만 일반적으로 10개가 필요한데 미리 100개를 만들어 항상 10% 미만 정도의 재사용률을 보인다면 그것은 메모리 낭비이다. 그러므로 다음과 같은 조건에서 Object Pool을 사용하고 관리하는 것이 좋겠다.

1. 비교적 큰 용량의 객체를 자주 사용되거나 반환해야하는 경우에 사용한다.
2. 미리 생성할 객체를 적당하게 정하도록 한다.

반대로 위 경우가 아니라면 쓸데없이 Object Pool을 사용하지 말자. 


5. Object Pool을 사용해보자.

폴리고널 lab에서 간단한 Object Pool을 공개했다. 아래 링크에서 다운로드 받을 수 있다.


사용 방법은 매우 간단하다. ObjectPool 클래스는 아래 코드처럼 특정 클래스를 할당(allocate)하면 된다.

var isDynamic:Boolean = true;

var size:int = 100;

var pool:ObjectPool = new ObjectPool(isDynamic);

pool.allocate(MyClass, size);


isDynamic 플래그는 Object Pool내의 객체가 비어있는가 체크하기 위해 사용한다. 즉, true이면 Pool은 자동적으로 정해진 크기(size)를 넘어서더라도 Pool의 크기를 확장하여 새로운 객체를 줄 수 있으나 false이면 정해진 크기만큼 객체를 이미 준 경우에는 Pool이 비게 되므로 Error를 발생시킨다. 

크기(size)를 100으로 지정했기 때문에 미리 Object Pool에 100개의 MyClass의 객체를 생성해 두도록 한다. 

이제부터 MyClass는 아래와 같은 방식으로 사용하면 되겠다.

myObjectArray[i] = new MyClass();

// 코드 대신 아래를 사용한다.

myObjectArray[i] = pool.instance;


다 사용한 MyClass객체는 Object Pool에 반환해줘야 한다. 이때 가비지 컬렉터의 기능을 이용하는게 아니라 재사용(recycle)을 위해서 다음과 같이 코딩하면 되겠다.

pool.instance = myObjectArray[i];


사용방법은 이게 전부다. 

원한다면 자신의 애플리케이션에 맞게 이 공개된 ObjectPool을 개선시킬 수 있다. 가령, 100개의 Pool을 만들어놓지만 처음부터 100개를 생성해놓지는 않겠고 필요할 때마다 생성해주는 구조인 게으른(lazy) 생성을 기대할 수 있을 것이다. 또는 DisplayObject의 경우라면 사용한다는 것을 DisplayObject.parent 속성으로 판단할 수 있다. 위 처럼 pool.instance = '다 사용한 객체'와 같은 형태로 반환하는 것보다 자신의 부모가 존재하지 않는다면 재사용할 객체로 판단하도록 만들면 일종의 자동 Object Pool을 만들 수 있다. 자동 Object Pool은 다음 링크를 참고하면 좋겠다. 

자동 풀링(auto pooling)을 구현해보자.



6. Object Pool과 new 연산자 사용 비교 

Object Pool을 사용해야하는 타당성은 퍼포먼스 실험을 통해 쉽게 알 수 있다.  이러한 점에서 폴리고널 랩에서 제공하는 실험은 매우 유용한 자료이다. 

실험 방법은 아래와 같다. (폴리고널 랩에서는 이 실험을 Window Vista, Flash Player 9.0.124에서 진행했다.) 그리고 Pool크기는 100으로 지정했다. 

첫번째로 아래 코드처럼 k수 만큼 Object Pool로 부터 객체를 get만 한다.

for (var i:int = 0; i < k; i++) instances[i] = p.instance;


두번째로 아래 코드처럼 k수 만큼 Object Pool로부터 객체를 get/set 한다.

for (i = 0; i < k; i++) instances[i] = p.instance;

for (i = 0; i < k; i++) p.instance = instances[i];


세번째로 아래 코드처럼 k수 만큼 Object Pool대신 new 연산자를 사용한다.

for (i = 0; i < k; i++) instances[i] = new MyClass();



결과는 다음과 같다.


네이티브 Object 클래스의 경우 Object Pool을 사용하는 것이 new 연산자를 사용한 것보다 5배 정도 빨랐다.


Object 보다 더 크고 복잡한 flash.geom.Point의 경우는 약간더 빠른 결과를 보인다.


 flash.display.Sprite 객체를 가지고 하면 무려 80배나 속도 개선이 되었다. 이는 복잡하고 큰 클래스에 Object Pool을 도입하면 효과적일 수 있다고 판단할 수 있다.

이 실험을 통해 적절한 방법으로 Object Pool을 사용하는 것은 애플리케이션의 퍼포먼스 증가에 지대한 영향을 줄 수 있다는 것을 깨달을 수 있다.


7. 메모리 사용의 폭이 급격한 애플리케이션은 좋지 못하다.
본인은 어떤 애플리케이션(Flash가 아니더라도)이든지 그 애플리케이션에 필요한 최소한의 메모리는 항상 존재한다고 생각한다. 무조건 메모리 사용을 줄여보겠다고 new, delete를 반복하는 것은 잘못된 것이다. new, delete를 반복하면 오히려 CPU 및 메모리 사용의 반복을 부축이며 애플리케이션의 전체적인 퍼포먼스를 저하시키는 경우가 발생할 수 있다. 실제로 new,delete를 반복하는 구조로 개발한 애플리케이션을 Flash Builder의 프로파일링 기능을 통해 메모리 사용을 살펴보면 큰폭으로 메모리 사용/해지가 일어난다는 것을 확인할 수 있다. 그러므로 몇몇 사람들이 Object Pool과 같은 개념과 같이 퍼포먼스 향상에 도움이 되는 개념을 전혀 사용하지 않고 Flash는 느리고 가비지 컬렉터 동작은 비정상적이다라고 말하는 것은 섣부른 판단이다라고 생각한다. 얼마든지 메모리를 효율적으로 사용하고 속도 개선을 할 수 있음에도 불구하고 잘못된 개발 방식때문에 전체적인 퍼포먼스가 나빠지는 것은 결국 개발자의 잘못인 것이다. 이는 Flash든 어떤 언어든 동일한 생각으로 접근해야 한다.

Flash Builder의 프로파일링 기능을 이용해 Object Pool과 new 연산자간의 메모리 변화를 확인할 수 있는 간단한 실험을 해보겠다. (프로파일링 기능을 사용하는 방법은 주제에서 벗어나므로 제외하겠다.)

먼저 Shape객체를 확장해서 크기와 색이 렌덤하게 그려지는 클래스를 만든다.

//화면에 출력할 Shape

class RandomShape extends Shape {

        static private var colorPool:Array = [0xff0000,0x00ffff,0x0000ff,0x00ff00,0x0f0f0f,0xf0f0f0,0xffff00,0xf00cf0,0x00fcaa,0xff0c9a];

        public function RandomShape():void {

        }

        public function draw():void {

               var radius:Number = Math.random() * 40;

               var color:uint = colorPool[ Math.floor(Math.random()*9) ];

               graphics.clear();

               graphics.beginFill(color,1.0);

               graphics.drawCircle( 0, 0, radius );

               graphics.endFill();

        }

}


위 클래스를 가지고 자동 Pool을 구현한 클래스를 만든다.(참고 : http://www.diebuster.com/?p=1000)

//Shape 자동 객체 Pool

class RandomShapeAutoPool {

        private var _pool:Vector.<RandomShape>;

        public function RandomShapeAutoPool() {

               //적당히 수로 초기화한다.

               _pool=new Vector.<RandomShape>();

        }

 

        public function getShape():RandomShape {

               var key:*, result:RandomShape;

               //먼저 기존의 pool에서 찾아본다.

               for (key in _pool) {

                       //만약 해당 객체가 null이라면 new 통해 생성한다.

                       if (_pool[key] === null) {

                              _pool[key]=new RandomShape;

                              result=_pool[key];

                              break;

                       //null 아니라면 parent 조사하여 사용가능한지 판단한다.

                       else if (_pool[key].parent === null) {

                              result=_pool[key];

                              break;

                       }

               }

               //기존의 pool안에서 쓸만한  찾지 못했다면

               if (result === null) {

                       result=new RandomShape;

                       _pool[_pool.length]=result;

               }

               //인스턴스를 반환한다.

               return result;

        }

}


이제 테스트할 수 있는 호스트코드를 제작해보겠다.

package {

        import flash.display.*;

        import flash.events.*;

        import flash.text.*;

        import flash.utils.*;

 

        [SWF(backgroundColor="#ffffff", frameRate="60", width="400", height="400")]

        public class ObjectPoolTest extends Sprite {

               private var pool:RandomShapeAutoPool=new RandomShapeAutoPool;

               private var poolFlag:Boolean = false;

               private var textPoolFlag:TextField;

               private var shapeCanvas:Sprite;

 

               public function ObjectPoolTest() {

                       addEventListener(Event.ADDED_TO_STAGE, ADDED_TO_STAGE);

               }

 

               private function ADDED_TO_STAGE($e:Event):void {

                       removeEventListener(Event.ADDED_TO_STAGE, ADDED_TO_STAGE);

                       stage.scaleMode=StageScaleMode.NO_SCALE;

                       stage.align=StageAlign.TOP_LEFT;

                       //rf)http://blog.jidolstar.com/656

                       if (stage.stageWidth === 0 && stage.stageHeight === 0) {

                              stage.addEventListener(Event.ENTER_FRAME,function($e:Event):void {

                                             if (stage.stageWidth > 0 || stage.stageHeight > 0) {

                                                     stage.removeEventListener($e.type, arguments.callee);

                                                     init();

                                             }

                                      });

                       else {

                              init();

                       }

               }

 

               private function init():void {

                       //shape 부모객체

                       shapeCanvas = new Sprite;

                       addChild( shapeCanvas );

                       //PoolFlag 상태표시

                       textPoolFlag = new TextField();

                       textPoolFlag.text = "poolFlag=" + poolFlag;

                       addChild(textPoolFlag);

                       //마우스 클릭 영역

                       graphics.beginFill(0x000000,0);

                       graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight);

                       graphics.endFill();

                       addEventListener(MouseEvent.CLICK, MOUSE_CLICK);

                       //반복동작     

                       addEventListener(Event.ENTER_FRAME, ENTER_FRAME);

               }

 

               private function MOUSE_CLICK($e:MouseEvent):void {

                       //마우스 클릭때마다 poolFlag 상태를 전환

                       poolFlag = !poolFlag;

                       textPoolFlag.text = "poolFlag=" + poolFlag;

               }

              

               private function ENTER_FRAME($e:Event):void {

                       var shape:RandomShape, i:int, numChildren:int;

                       //poolFlag 따라서 new연산자 또는 Object Pool에서 객체 참조

                       shape = poolFlag ? pool.getShape() : new RandomShape;

                       shape.draw();

                       shape.x = Math.random() * stage.stageWidth;

                       shape.y = 0;

                       shapeCanvas.addChild( shape );

                       //계속 아래로 떨어뜨림화면에서 벗어나면 removeChild시킴

                       numChildren = shapeCanvas.numChildren;

                       for( i=0; i<numChildren;i++) {

                              shape = shapeCanvas.getChildAt(i) as RandomShape;

                              shape.y += 5;

                              if( shape.y > stage.stageHeight ) {

                                      shapeCanvas.removeChild( shape );

                                      i--;

                                      numChildren--;

                              }

                       }

               }

        }

}


동작은 매우 단순하다. poolFlag가 true이면 제작한 자동 Object Pool을 사용하겠다는 것이고 false이면 new 연산자를 사용해 RandomShape객체를 생성하겠다는 것이다. poolFlag 전환은 마우스 클릭으로 할 수 있다. 그리고 비가 내리는 것처럼 Shape 객체는 위에서 부터 아래로 떨어지며 맨 아래에 닿으면 removeChild 시킨다.

테스트 화면


실행 코드와 결과물은 http://wonderfl.net/code/805dc65d1f7800b7bee5dc3cd72ca21c0a87c2d6 에서도 볼 수 있다.

이제 Flash Builder의 프로파일링 기능을 이용해 메모리 변화 및 객체 생성빈도를 살펴보자.

먼저 new 연산자를 사용했을때 메모리 변화를 보자. 

new 연산자로 객체생성시 Memory Usage


new 연산자를 사용해 객체를 생성하고 필요없을때 removeChild를 하게 되면 가비지 컬렉션 처리가 되기 때문에 일정한 시점에 메모리가 해지되는 것을 반복하는 것을 확인할 수 있다.

new 연산자로 객체생성시 Live Object


위 표는 축적된 객체수(Cumulative Instances), 현재 객체수(Instances), 축적된 메모리(Cumulative Memory), 현재 메모리(Memory)를 나타낸다. 변화 무쌍하며 실제로 보여지지 않는 객체도 가비지 컬렉션 처리가 일어날때까지 메모리에 남아 있다. 분명 비효율적으로 보인다.

반대로 Object Pool을 이용했을때 메모리 변화이다.

Object Pool로 객체관리시 Memory Usage

new 연산자를 사용했을때와 비교가 안될 정도로 고요한 느낌이 든다. 거의 일직선의 메모리 사용율을 보인다.

Object Pool로 객체관리시 Live Object


축적되는 객체도 81개를 넘지 않는다. 그만큼 객체 생성과 삭제에 민감하지 않게 되고 가비지 컬렉터 도움을 받지 않고 객체관리가 되는 것을 확인할 수 있다.


8. 재사용할 수 있는 클래스 제작

Object Pool을 사용시 한가지 고려할 사항은 재사용할 수 있는 클래스를 만들어야 한다는 점이다. 왜냐하면 Object Pool은 단순히 재사용을 위한 공간만 제공하는 것이지 재사용을 하기 위한 어떤 기능을 클래스에게 줄 수 없기 때문이다. 그래서 재사용이 가능한 클래스를 설계하는 것은 하나의 기술적 이슈가 될 수 있다.  

재사용할 수 있는 클래스를 어떻게 만드는지 간단한 예를 들어보겠다.

final public class MyData {

        public var type:String;

        public var id:int;

        public var name:String;

}


위 클래스는 데이터를 담는 클래스이다. id, name은 실제 사용하는 데이타 값들이고 이 데이타의 종류는 type으로 구분한다. 이 데이타를 사용하는 인터페이스를 제작해보겠다.

public interface IMyClass {

        function init($data:MyData):void;

        function clear():void;

        function get data():MyData;

}


이 인터페이스는 앞으로 만들 MyClass01, MyClass02... 등을 구현하기 위한 것이다. 초기화 하기 위해 init()함수가 있고 인자로 위에서 제작한 MyData 객체를 받는다. 또한 사용할 필요가 없을때 내부 청소를 위해 clear()함수를 추가했다. 게다가 가지고 있는 data를 참고하기 위해 get data()도 만들었다. 이제 이 인터페이스를 구현한 클래스는 아래처럼 만든다.

final internal class MyClass01 extends Sprite implements IMyClass {

        private var _data:MyData;

        public function MyClass01() {

        }

        public function init($data:Mydata):void {

               _data = $data;

               //구현

        }

        public function clear():void {

               _data = null;

        }

        public function get data():MyData {

               return _data;

        }

}

 

final internal class MyClass02 extends Sprite implements IMyClass {

        private var _data:MyData;

        public function MyClass02() {

        }

        public function init($data:Mydata):void {

               _data = $data;

               //구현

        }

        public function clear():void {

               _data = null;

        }

        public function get data():MyData {

               return _data;

        }

}

 

final internal class MyClass03 extends Sprite implements IMyClass {

        private var _data:MyData;

        public function MyClass03() {

        }

        public function init($data:Mydata):void {

               _data = $data;

               //구현

        }

        public function clear():void {

               _data = null;

        }

        public function get data():MyData {

               return _data;

        }

}



코드가 길어보이지만 3개 클래스 모두 IMyClass를 구현했고 Sprite를 확장했다. 단지 클래스 이름만 다르며 실제 구현부는 알아서 구현하면 된다. 

개발자는 MyClass01, MyClass02, MyClass03은 매우 자주 new 연산자로부터 생성되는 클래스로 판단했고 그래서 Object Pool을 도입하겠다고 결정했다고 하자. 그럼 다음과 같이 시도해 볼 수 있다.

import de.polygonal.core.ObjectPool;

import flash.display.DisplayObject;

 

public class MyPool {

        private static var poolList:Object;

       

        static public function init():void {

               poolList = {

                       'type01':new ObjectPool(true),

                       'type02':new ObjectPool(true),

                       'type03':new ObjectPool(true),

               };

               poolList.type01.allocate(20, MyClass01);

               poolList.type02.allocate(10, MyClass02);

               poolList.type03.allocate(100, MyClass03);

        }

       

        static public function getObject($data:MyData):IMyClass {

               if( $data === null ) {

                       throw new Error('인자값은 null이면 안됩니다.');

               }

               try {

                       var object:IMyClass = poolList[$data.type].object;

                       object.init($data);

               catch {

                       throw new Error('데이타의 type값이 잘못된 값입니다.');

               }

               return object;

        }

       

        static public function returnObject($object:IMyClass):void {

               if( $object === null ) {

                       throw new Error('인자값은 null이면 안됩니다.');

               }

               $object.clear();

               if( DisplayObject($object).parent ) {

                       DisplayObject($object).parent.removeChild( DisplayObject($object) );

               }

               poolList[$object.data.type].object = $object;

        }

}



위에서 제작된 MyPool은 static클래스이며 ObjectPool을 이용해 IMyClass 인터페이스를 구현한 클래스를 init()함수에서 적당하게 할당하는 것을 확인할 수 있다. 또한 getObject()를 통해 인자값 data를 참고하여 참조할 Object Pool을 찾아 IMyClass를 구현한 각각의 클래스의 객체를 받아올 수 있으며 returnObject를 통해 다 사용한 객체를 반환한다. 

실제로 실무에서 이런 형태로 개발했고 이는 매우 유용했다. MyPool 클래스를 더 개선해서 더 많은 수 Object Pool을 감당할 수 있도록 확장할 수 있다면 더욱 유용해질 것이라 생각한다.


9. 정리하며 

Object Pool 개념은 Flash에만 국한되는 유용한 개념이 아니다. 이런 개념들에 대한 노하우를 계속 쌓아간다면 당신의 애플리케이션은 더욱 좋은 작품으로 탈바꿈할 수 있을 것이다. 

Flash Player가 느리고 메모리 관리가 안된다고 생각하지는 말자. Flash Player 태생자체가 그런것을 어찌하겠는가? 제작한 애플리케이션이 느리다면 그것은 결국 개발자가 잘못한 것임을 항상 인지하자. 중요한 것은 여전히 Flash Player는 유용하고 많이 사용되고 있으며 지금도 나날이 발전하여 Cross OS, Cross Browser를 넘어 Cross Device 세계로 뻗어가고 있다는 점이다.


10. 참고글

글쓴이 : 지돌스타(http://blog.jidolstar.com/666

Posted by 1010
00.Flex,Flash,ActionScript2014. 8. 20. 14:30
반응형

나는 최근에 [Flex / AIR / ActionScript3] Event 청취자와 메모리 관리 에 대한 글을 썼다.

그러나 놀랍게도 "불꽃남자"님께서 다음과 같이 지적해주셨다.

Flex의 메모리 문제에 대한 마지막 방법이
weakly reference로 모든 객체를 다루는것이지만...
무수한 테스트에 의해도....
결국 메모리는 털어지지 않습니다.
일반적인 OOP에서 메모리를 sweep하는 프로세스는
스레드가 담당을 하게 되고...그 스레드만의 stop the world를
통해 다른 실행중인 스레드가 영향을 받지 않게 해야지만,
AS3의 GC에는 그러한 구조가 없습니다.
weakly reference가 잘 동작하지 않는다고 Adobe측에 아주 강력하게
항의를 했지만, 돌아오는 답은 원래 그런거다...라는...
어이가 없죠?ㅋㅋㅋ
Flex Product Manager에게도 강력히 요청했으나.
Flex 3에서도 변한건 없습니다.
Large Application에서 GC를 정확히 할려면,
편법으로 밖에 할 수 없겠죠...

참고로..Flex SDK안의 많은 오브젝트에 addEventListner가 구현되어있지만,
removeEventListner가 없는 코드가 더 많다는..ㅡ,ㅡ;;
이부분 역시 어도비에 따졌지만,
돌아오는 답은...원래 그런거다..그냥 써라..
역시 어이가 없죠?ㅋㅋㅋ
자기들이 가이드라인해준 방식을 SDK안에서 조차 적용이 안되어있는걸 
어떻게 설명해야하나요?ㅡ,ㅡ;;

아..그리고 위의 코드에서
objects.destroy();
objects = null;
을 호출하게 되면, 이론상으로 잘 돌아가야는데...
원래 OOP의 CG구조가 저렇게 destroy()를 시키고, null을 대입하면
바로 GC가 일어나는건 아닙니다.
하지만 최 우선순위를 두고 메모리에서 없애야 하는데,
Flash Player에서 그 타이밍이 언제인지는 아무도 모릅니다.
다만, 

어도비에서 주장하는 바는 웹브라우저가 가용할 수 있는메모리, 혹은
Flash Player가 가용할 수 있는 메모리의 임계치까지 다 찼을때만
GC가 발생한다고 합니다.


이게 말이나 되는 일입니까?ㅎㅎ
그럼 destroy() 메소드는 왜 만들었으며, 객체에 null을 대입하는
코드는 무슨 소용이겠습니까?
좋은글 올려주셨는데, 우리가 생각하는데로 Flex가 동작하지 않기에
몇마디 달아봤습니다.ㅎㅎㅎ
수고하세요 지돌스타님~~~^^

용호님이 테스트 하셔서 좋은 정보 올려주세요.
그리고 위에서 보여주신 정보는 상당히 좋은 정보입니다.
보통 자바개발자라면 저러한 부분을 생각하지만,
그렇지 않은 분들도 많거든요.
외국에서도 weakly reference하게 개발하자고 많은 분들이 주장하시고 있고,
말씀하신 remove관련된 내용은 분명히 어플리케이션의 부하를 줄여주는
좋은 방법입니다.
메모리 문제 때문에 악받은 Flex 챔피언들 몇명있죠..ㅋㅋ
동호대표, 진욱대표...그리고 저..ㅋㅋㅋ
어도비 수석 엔지니어의 말로는 Flash Player 10이 나오면
해결 가능할것 같다...같다...라고 했습니다..ㅎㅎ



결국 무슨말이고하니.... 
이런식으로는 가비지 컬렉터(GC)가 바로 동작하지 않는다는 것이다.
그래서 실제로 그러한가 테스트 해보았다.
그랬더니... 정말 그러한 것이 아닌가?

수백개의 자식 컴포넌트를 추가한뒤 참조까지 삭제후 부모에서 떼어냈는데도... System.totalMemory 값은 거의 불변이었다. 재미있는 것은 다시 수백개의 자식을 추가하는 동안 갑자기 System.totalMemory가 증가만 하는것이 아니라 뚝 떨어지는 시기가 있다. 즉, 프로그래머는 메모리 관리는 내 마음대로 할 수 없다는 것을 의미한다. Flash Player가 알아서 어느정도 GC 대상이 만들어졌을 경우 삭제한다는 것이다.

한가지 중요한 점은 [Flex / AIR / ActionScript3] Event 청취자와 메모리 관리 글에서 쓴 것처럼 최소작업은 해야 그나마 Flash Player의 GC가 동작할 수 있다는 것이다. 그래서 쓸데없는 글은 아니였구나 생각했다. 

더 재미있는 것이 있다.
바로 System.gc() 함수이다.
이 함수는 GC를 강제적으로 실행해준다. 그...그런데....

gc()method 
public static function gc():void

Forces the garbage collection process.

For the Flash Player debugger version and AIR applications only. In an AIR application, the gc() method is only enabled in content running in the AIR Debug Launcher (ADL) or, in an installed applcation, in content in the application security sandbox.

Player Version: Flash Player 9 Update 3 or the AIR Debug Launcher (ADL).

Flash Player debugger와 AIR Applications 에서만 동작한단다.... 
즉, 배포용 Flash/Flex 프로그램에서는 동작하지 않는다는 말이다.
왜 이렇게 만들었지???? ㅡㅡ;;;;;

테스트 소스를 받아봐서 실행해보길 바란다(Flex 3기반)



 이 소스는 http://wooyaggo.tistory.com/search/system.gc 에서 제공한 소스를 참고했다.

만약 Flex나 Flash 프로그래머라면 Flash Player가 debug버전으로 설치되어 있을 것이다. 그 상태에서는 System.gc()가 잘 동작하므로 GC가 매우 잘 동작하는 것처럼 보일것이다. 하지만 

http://wooyaggo.tistory.com/112 

에서 Flash Player Uninstaller를 이용해 debug용 Flash Player를 삭제한 후 보통 Flash Player 를 설치후 방금 받은 소스를 실행해보자.
그럼 System.gc()는 제대로 동작안되고 GC가 동작되는 시기가 Flash Player에서 임의로 정해진다.

결론적으로 이러한 환경에서는
되도록이면 객체를 최소한 만들고 
모듈화를 최대한 활용하며 
재활용할 수 있는 것은 재활용하면서 사용하는 것이 
메모리관리에 도움된다.

아~~~ 다 좋은데.... 왜 메모리가 이렇게 골치아프게 만드는 것이냐.... ㅡㅡ;;

잘못된 내용이나 추가할 사항이 있다면 언제든지 지적해주세요.
이러한 사항은 함께 공유해야 한다고 생각하거든요.

읽을거리 : AIR/Flex: Memory optimisation 

글쓴이 : 지돌스타(http://blog.jidolstar.com/319)

Posted by 1010
00.Flex,Flash,ActionScript2014. 8. 20. 14:27
반응형
참고 : Adobe Flex3 실전 트레이닝 북, 해외 블로그, 국내 블로그
이 글은 어도비리아에 기술문서로 제출 되었으며, 동일한 내용을 어도비리아에서 볼 수 있습니다.



-2부 프로파일링-
<목차>
1. 프로파일링이란?
2. 가비지 콜렉션
2-1. 레퍼런스 카운팅
2-2. 마크앤스윕
3. Flex Profiling perspective
3-1. 메모리 프로파일링
3-1-1. Profile 뷰
3-1-2. Memory Usage 뷰
3-1-3. Live Objects 뷰
3-1-4. Memory Snapshot 뷰
3-1-5. Object References 뷰
3-1-6. Loitering Objects 뷰
3-1-7. Allocation Trace 뷰
3-1-8. Object Statistics 뷰
3-2. 퍼포먼스 프로파일링
3-2-1. Performance Profile 뷰
3-2-2. Method Statistics 뷰
4. 맺음말


1. 프로파일링 이란?
애플리케이션을 개발하고 실행하게 되면 알 수 없는 원인에 의해 느려지거나 심지어 멈추는 경우가 종종 발생한다. 디버깅만으로는 이런 알 수 없는 원인을 찾아내기란 쉽지 않다. 이런 경우 애플리케이션의 성능측정을 통해 어느 부분에서 어떤 문제가 발생하는지를 찾아낼 수 있다. 성능측정을 전문적인 용어로 프로파일링 이라고 한다.
프로파일링은 디버깅과 더불어 소프트웨어 품질 향상에 도움을 준다. 

우리는 Flex Builder3 Professional(이하 플렉스빌더) 에서 제공하는 프로파일링 기능을 이용할 것이다.
(Flex Builder Standard 버전은 프로파일링 기능을 제공하지 않는다.)
플렉스빌더에서 프로파일링을 하기전 플래시 플레이어의 가비지 콜렉션에 대한 이해가 필요하다.
따라서 먼저 가비지 콜렉션에 대해 알아보겠다.


2. 가비지 콜렉션
가비지 콜렉션이란, 플래시 플레이어의 숨겨진 프로세스인 "가비지 콜렉터"에 의해서 참조 되지 않는 객체들을 메모리에서 해제 하는 것을 말한다. 참조 되지 않는 객체가 있다고 해서 
가비지 콜렉션이 무조건 수행되는건 아니다.가비지 콜렉션이 언제 어느 시점에서 일어나는지 정확하게 알 수는 없지만 짐작할 수 있는 시점은 현재 애플리케이션이 플래시 플레이어가 OS로부터 빌려온 메모리 보다 더 많은 메모리를 요구할 때 이다.

가비지 콜렉터는 객체가 더 이상 참조되지 않는지 확인하기 위해 레퍼런스 카운팅과 마크앤스윕 이라는 두 가지 절차를 따른 후 가비지 콜렉션을 수행한다. 이 두 가지 절차를 이해하는 것은 플렉스 프로파일링 기능을 이용하는데 도움을 줄 것이고 메모리를 효율적으로 이용하는 애플리케이션을 작성하도록 도움을 줄 것이다.

그럼 가비지 콜렉션을 위한 두 가지 절차에 대해 알아보자.


2-1. 레퍼런스 카운팅
레퍼런스 카운팅은 ActionScript 1.0 때 부터 사용되어 왔으며, 객체가 가비지 콜렉션의 대상인지 확인하는 가장 쉽고 빠른 방법이다. 어떤 객체를 생성하고 
참조값(해당 객체의 메모리 주소 값)을 레퍼런스(참조변수)에 할당하면 레퍼런스 카운트가 1 증가한다. 이 객체의 참조값을 또 다른 레퍼런스에 할당하면 레퍼런스 카운트가 1 증가되어 총 2가 된다. 만약 두개의 레퍼런스중 하나의 레퍼런스에 null을 할당하게 되면 레퍼런스 카운트는 1이 감소하며, 남은 하나의 레퍼런스 마저 null을 할당하면 레퍼런스 카운트는 0이 되어 버린다. 가비지 콜렉터는 레퍼런스 카운트가 0인 것들을 가비지 콜렉션의 대상으로 삼는다. 

다음 예제를 보면 쉽게 이해할 수 있을 것이다.



위 예제에서 생성된 Object는 레퍼런스 카운트가 0이기 때문에 가비지 콜렉션의 대상이 된다.
하지만 레퍼런스 카운팅 만으로는 상호참조에 대한 문제를 해결할 수 없다.

상호참조가 무엇인지 다음 예제를 통해 살펴보자.



위 예제에서 생성된 두개의 Object는 obj.foo와 obj2.foo 에서 상호참조 되고 있다.
obj와 obj2에 null을 할당하더라도 이 두개 Object의 레퍼런스 카운트는 결코 0이 될 수 없다.
이런 상황은 매우 빈번하게 일어나며 더욱 복잡해질 수 있다. 플래시 플레이어7 까지는 XML 객체에서 노드들의 상호참조 때문에 이슈가 되기도 했는데, 다행히 플래시 플레이어8에서 마크앤스윕 이라는 새로운 방법이 등장해 이 문제를 해결하고 있다.


2-2. 마크앤스윕
플래시 플레이어(버전 8이상) 는 루트객체(Application 자체 또는 메인MXML) 부터 시작해서 루트객체가 참조하는 객체들과 그 객체들(참조 당하는 객체들)이 참조하는 객체들을 마킹한다. 여기서 마킹의 의미는 객체가 가비지 콜렉션의 대상이 되지 않게 하기 위해 점 찍어두는 것이다. 이 처럼 플래시 플레이어는 애플리케이션의 모든 객체트리를 돌며 참조되고 있는 객체들을 마킹한다. 마킹된 객체들은 레퍼런스 카운트가 무조건 1이상이기 때문에 결코 가비지 컬렉션의 대상이 되지 않는다. 반면 마킹되지 않은 객체들은 가비지 컬렉션의 대상이 된다. 마킹되지 않은 객체는 레퍼런스 카운트가 0일거라고 생각할 수 있지만 그렇지 않다. 플래시 플레이어는 오직 루트객체 까지 연결된 객체만 마킹하므로, 루트객체 까지 연결되지 않은 상호참조 객체들은 마킹되지 않는다. 따라서 상호참조 객체들은 레퍼런스 카운트가 1이상이라도 루트객체에 연결되어 있지 않으면 가비지 콜렉션의 대상이 된다.

다음 그림을 통해 마킹된 객체와 마킹되지 않은 객체들을 파악해보자.



(위 그림은 어도비에서 제공하는 가비지 콜렉터 시뮬레이션이다.http://www.adobe.com/devnet/flashplayer/articles/garbage_collection/example2.html 에서 직접 시뮬레이션 해볼 수 있다.)

위 그림에서 초록색은 루트객체에 연결된 마킹된 객체들이고 빨간색은 상호참조로 인해 레퍼런스 카운트가 각각 1 이지만 루트객체에 연결되지 않았기 때문에 가비지 콜렉션이 수행되면 빨간색 객체들은 메모리에서 제거될 것이다.

지금까지 플래시 플레이어에서 메모리가 어떻게 관리되는지 가비지 콜렉션이 언제 수행되는지 그리고 가비지 콜렉션이 수행될 때 거치는 두가지 과정에 대해 알아봤다. 이러한 밑 바탕이 되는 지식과 함께 본격적으로 플렉스 프로파일링을 시작해 보겠다.


3. Flex Profiling perspective
Flex Profiling perspective 는 플렉스빌더에서 제공하는 프로파일링 관련 도구들을 모아놓은 작업 환경이다.
이 작업 환경에서 실시간으로 애플리케이션이 사용하는 메모리를 비롯해 생성된 객체들의 수를 확인할 수 있는 메모리 프로파일링과, 함수 수행 시간을 확인할 수 있는 퍼포먼스 프로파일링을 할 수 있다.


3-1. 메모리 프로파일링

메모리 프로파일링은 일반적으로 애플리케이션의 메모리가 계속 증가 할 때 이용한다.
메모리가 계속 증가하는 이유는 더 이상 사용되지 않는 객체들이 어디선가 참조되고 있어서 가비지 콜렉터가 제거하지 못하기 때문이다. 이러한 현상을 보다 전문적인 용어로 메모리 누수(memory leak) 라고 부른다.

메모리 프로파일링은 메모리 누수를 찾을수 있을뿐만 아니라, 어디서 어떤 객체가 생성되고 있으며 객체들이 얼마 만큼의 메모리를 차지하고 있는지 알 수 있다. 또한 특정한 두개의 서로 다른 시점의 메모리 상태를 저장하고 이를 비교분석 할 수도 있다. 

먼저 정상적인 애플리케이션을 통해 플렉스빌더의 프로파일링 도구를 살펴보자.
아래의 코드를 프로파일링 모드로 실행한다. (디버그 실행버튼 바로 오른쪽 버튼)



프로파일링 실행버튼을 누르면 다음과 같은 창이 나타난다.



위 창은 프로파일링 옵션을 묻는 창이다. 메모리 프로파일링의 경우 두가지 옵션이 더 존재하는데 Watch live memory data는 실시간으로 현재 활동중인 객체와 메모리 크기를 감지하고 지금까지 생성되었던 객체와 메모리의 크기를 알 수 있는 옵션이며, Generate object allocation stack traces는 특정한 두 시점 사이에서 호출된 메소드와 그 메소드에서 사용했던 메모리 크기를 알 수 있는 옵션이다. 모든 체크박스를 선택하게 될 경우 프로파일링 데이터 수집을 그 만큼 더 많이 하게 되기 때문에 애플리케이션의 속도가 느려진다. 지금은 위 그림처럼 메모리 프로파링일 항목만(하위 두개의 옵션 포함) 체크하고 Resume 버튼으로 프로파일링을 시작해보자.

프로파일링이 시작되면 플렉스빌더는 프로파일링 모드로 전환되고 다음 그림처럼 보일 것이다.



①번 영역은 프로파일링 목록과 프로파일링 실행을 제어할 수 있는 [Profile 뷰] 이다.
②번 영역은 현재 메모리 상황을 그래프로 표현하는 [Memory Usage 뷰] 이다.
③번 영역은 실시간으로 메모리에 존재하는 객체들을 볼 수 있는 [Live Objects 뷰] 이다.

그럼 먼저 프로파일링을 시작했을 때 기본적으로 보여지는 위 세가지 뷰에 대해 자세히 알아보겠다.


3-1-1. Profile 뷰
프로파일링이 시작된 이후에, 프로파일링을 일시중지 하고 다시 시작 하는 등 실행을 제어 하거나, 메모리와 퍼포먼스 분석을 위한 스냅샷 저장을 할 때 [Profile 뷰] 이용한다. [Profile 뷰] 는 프로파일링 실행을 제어할 수 있는 컨트롤 부분과 프로파일링이 진행중 이거나 이미 중단된 애플리케이션과 스냅샷의 목록이 나타나는 부분이 있다. 애플리케이션 목록 중 하나를 선택하고 수행하길 원하는 버튼을 클릭함으로써 프로파일링을 제어하게 된다. 그럼 실행을 제어하는 컨트롤 부분의 버튼들이 어떤 기능을 하는지 알아 보도록 하겠다.

 Resume
일시 정지된 애플리케이션과 함께 프로파일링을 다시 계속한다.

 Suspend
애플리케이션과 함께 프로파일링을 일시 정지한다.

 Terminate
프로파일링을 종료 한다. 애플리케이션(브라우저)는 종료되지 않는다.

 Run Garbage Collector
강제적으로 가비지 콜렉션 수행한다. 이 버튼을 누르면 [Memory Usage 뷰] 에서 가비지 콜렉션이 수행된 것을 알 수 있다.

 Take Memory Snapshot
현재 실행중인 애플리케이션의 메모리 상황을 저장하고, [Profile 뷰]의 선택된 애플리케이션 하위로 메모리 스냅샷 항목이 추가된다. 이 버튼을 누르면 가장 먼저 가비지 콜렉션을 수행한 뒤 메모리 상황을 저장한다. 가비지 콜렉션이 먼저 수행되는 이유는 가비지 객체들이 메모리 상황을 분석하는데 있어서 잘못된 정보로 활용되기 때문이다. 메모리 스냅샷은 1개 이상 저장할 수 있고, 저장된 스냅샷 목록중 하나를 더블클릭 했을 때 열리는 [Memory Snapshot 뷰] 를 이용해 그 당시 메모리 상황을 분석할 수 있다. 또한 저장된 스냅샷 목록 중 두개의 스냅샷을 한 쌍으로하여 앞으로 나오게 될 두 시점 사이의 비교 기능을 이용할 수 있다. 자세한 내용은 
3-1-4. Memory Snapshot 뷰, 3-1-6. Loitering Objects 뷰, 3-1-7. Allocation Trace 뷰 이 세 파트에 걸쳐서 다루겠다.

 Find Loitering Objects
이 버튼은 메모리 스냅샷 목록 중 두개를 선택 했을 때 작동 된다. 이 버튼은 선택된 두 메모리 스냅샷을 비교하여 두 시점 사이에서 생성된 객체들을 확인할 수 있는 [Loitering Objects 뷰]를 연다. 자세한 내용은 
3-1-6. 
Loitering Objects 뷰
 파트에서 다루겠다.

 View Allocation Trace
이 버튼은 프로파일링을 시작할 대 실행 옵션을 묻는 창에서 Generate object allocation stack traces 항목을 체크하고, 메모리 스냅샷 목록 중 두개를 선택 했을 때 작동 된다. 이 버튼은 선택된 두 메모리 스냅샷을 비교하여 두 시점 사이에서 수행된 메소드들을 확인할 수 있는 [Allocation Trace 뷰]를 연다. 자세한 내용   3-1-7. Allocation Trace 뷰 파트에서 다루겠다.

 Reset Performance Data
현재까지 플렉스빌더가 수집한 퍼포먼스 데이터를 초기화 한다. 플렉스빌더는 이 버튼이 눌린 시점으로 하여 퍼포먼스 데이터를 새로 수집하게 된다.

 Capture Performance Profile
프로파일링이 시작된 이후 또는 퍼포먼스 데이터가 초기화된 시점부터 현재까지 수집한 퍼포먼스 데이터를 저장하고, [Profile 뷰]의 선택된 애플리케이션 하위로 퍼포먼스 스냅샷 항목을 추가한다. 추가된 퍼포먼스 스냅샷 항목을 더블클릭 하면 성능에 관련된 정보를 보여주는 [Performance Profile 뷰]가 열린다. 자세한 내용은 3-2. 퍼포먼스 프로파일링 파트에서 다루겠다.

 Delete
선택된 스냅샷 항목 또는 프로파일링이 종료되고 선택된 애플리케이션을 목록에서 제거한다.


3-1-2. Memory Usage 뷰
만약 여러분의 애플리케이션에서 메모리가 정상적인지 확인하고 싶다면 가장 먼저 [Memory Usage 뷰]를 확인하라. [Memory Usage 뷰]를 통해 플래시 플레이어의 메모리 상황을 한눈에 파악할 수 있기 때문에 메모리 누수가 발생하는지 가장 쉽고 빠르게 알 수 있는 도구이다.



위 그림에서 빨간색 라인은 애플리케이션이 시작된 이후로 가장 많은 메모리를 사용했던 양을 나타내고, 파란색 라인은 현재 사용중인 메모리 양을 나타낸다. 위 그래프는 매우 정상적인 흐름의 톱니 패턴을 띄고 있다. 이 톱니 패턴은 앞서 설명 했듯이 애플리케이션이 플래시 플레이어가 OS로 부터 빌려온 메모리 보다 더 많은 메모리를 요구할 때 가비지 콜렉션이 수행된 흔적이다.


3-1-3. Live Objects 뷰
애플리케이션에 존재하는 객체들을 실시간으로 확인하고 싶을 때 [Live Objects 뷰]를 이용한다. 이 뷰는 프로파일링이 진행되는 동안 애플리케이션에서 실시간으로 생성되는 객체와 그 객체가 얼마나 많은 메모리를 사용하는지 보여준다. 또한, 그것들에 대한 누적된 수치도 함께 보여준다.



위 그림에서 알 수 있듯이 [Live Objects 뷰]에는 6개의 컬럼이 존재한다. 이 중 Class, Package 컬럼은 궂이 설명하지 않아도 어떤 컬럼인지 알 수 있을 것이다. 그 두개를 제외한 나머지 컬럼들에 대해 알아보자.

Cumulative Instances
프로파일링이 시작된 이후로 생성된 객체의 누적 합계이다.

Instances
현재 메모리에 존재하는 객체의 수이다.

Cumulative Memory
프로파일링이 시작된 이후로 생성된 객체가 사용했던 메모리 양의 누적 합계이다.

Memory
현재 메모리에 존재하는 객체가 사용중인 메모리 양이다.


위 그림에서 처럼 Package 컬럼 값이 없거나 그 외에 생소한 값을 가진 항목들은 우리가 만든 애플리케이션 단계의 정보가 아니기 때문에 그다지 유용한 정보가 되지 못한다. 지금 우리에게 필요한 정보는 예제 코드에서 1초마다 100개씩 생성되고 있는 DataGrid 객체에 대한 정보일 것이다. 하지만 기본적으로 적용된 필터 때문에 우리에게 필요한 DataGrid 객체에 대한 정보를 볼 수 없기 때문에 필터를 수정해야 한다. 

먼저 위 그림의 우측 상단의 빨간색 마크된 부분의 세번째 아이콘을 누르면 Filters 창이 나타나는데, Inclusion filters와 Exclustion filters를 다음과 같이 변경하자. 그러면 필터의 포함 항목과 제외 항목을 통해 적절하게 필터링된 객체들이 보여질 것이다.



DataGrid 클래스의 패키지인 mx.controls.* 패키지를 포함 시켰다면 다음과 같이 [Live Objects 뷰]에 DataGrid 하나만 나타날 것이다.



위 그림에서 처럼 DataGrid 객체가 지금까지 어마어마하게 생성 되었으며, 현재 메모리에서 존재하는 객체의 수를 비롯해 사용중인 메모리 양도 쉽게 알 수 있다. 이처럼 [Live Objects 뷰]를 통해 실시간으로 생성되거나 소멸되는 객체들 뿐만 아니라, 누적된 수치까지 확인함으로써 애플리케이션의 전반적인 메모리 상황을 알 수 있고, 이러한 정보는 여러분의 애플리케이션을 깊게 이해하는데 도움이 될 것이다.


※코드 변경하기
지금까지 프로파일링이 시작되었을 때 구성되는 기본적인 뷰들에 대해 알아봤다. 이제 예제 코드를 변경한 후 [Memory Usage 뷰]를 통해 메모리 누수를 확인할 것이다. 그리고 앞으로 나오게 될 [Memory Snapshot 뷰] 와 [Loitering Objects 뷰] 그리고 [Allocation Trace 뷰]를 이용해 어디에서 메모리 누수가 발생하는지 확인할 것이다. 변경된 코드는 다음과 같다.


코드를 변경했다면 프로파일링을 시작하고 [Memory Usage 뷰]를 관찰해 보자. 다음 그림처럼 메모리가 계속 상승하는 그래프를 볼 수 있을 것이다. 이렇게 메모리가 계속 상승하는 이유는 가비지 콜렉션이 수행될 때 메모리에서 제거 되어야할 객체들이 어디선가 계속 참조되고 있기 때문이다.



변경된 예제코드에서 직접 메모리 누수 부분을 찾을 수 있지만 우리는 그 사실을 모른다고 가정하고 메모리 프로파일링을 통해 어디에서 메모리 누수가 발생하는데 찾아볼 것이다.

3-1-4. Memory Snapshot 뷰
여러분이 과거 특정한 시점에 저장했던 메모리 상황을 알고 싶을 때 [Memory Snapshot 뷰]를 이용한다. 이 뷰는 특정한 시점의 메모리 상황을 보여 주는 것이기 때문에 [Live Objects 뷰] 처럼 메모리 상황을 업데이트 하지 않고, 오직 해당 시점의 메모리 상황만 보여주는 정적인 뷰이다. [Profile 뷰]에서 저장된 메모리 스냅샷 항목을 더블클릭 하면 이 뷰를 열 수 있다.

이제 위 예제코드의 문제점을 찾아보자. [Memory Usage 뷰] 에서 메모리 누수를 포착 했다면 가장 먼저 [Live Objects 뷰]를 통해서 어떤 객체가 메모리에서 계속 증가하는지 발견해야 한다. 여러분은 이미 [Live Objects 뷰]에 대한 사용법을 알고 있고, 직접 확인해보면 DataGrid 객체가 계속 증가하는 것을 확인할 수 있을 것이다. 확인 했다면 Take Memory Snapshot 버튼으로 메모리 스냅샷을 만들고, 추가된 메모리 스냅샷을 더블클릭 하여 [Memory Snapshot 뷰]를 열어보자. [Memory Snapshot 뷰]를 열었다면 다음 그림처럼 DataGrid 객체만 나오도록 앞서 배운 필터를 설정해보자.



[Memory Snapshot 뷰]에서 제공하는 컬럼들은 앞서 배운 [Live Objects 뷰]에 이미 있는 것들이다. 우리는 현재까지 [Memory Usage 뷰]를 통해 메모리 누수를 발견하고 [Live Objects 뷰]를 통해 DataGrid 가 계속 생성되는 것을 확인했다. 이제는 그 문제의 DataGrid 객체가 과연 어디에서 생성되는지 알아야할 때 이다. 위 그림에서 알 수 있듯이 [Memory Snapshot 뷰] 자체만으로는 DataGrid가 어디서 생성되는지 알 수가 없다. 하지만 이 뷰에서 특정 로우를 더블클릭 하거나, 특정 로우를 선택한 상태에서 우측상단 빨간색 박스 부분의 첫번째 아이콘을 누르면 객체가 어디에서 생성되었는지 알려주는 [Object References 뷰]를 열 수 있다. 자세한 내용은 바로 다음 3-1-5. Object References 뷰 파트에서 다룬다.

3-1-5. Object References 뷰
메모리 프로파일링을 하는 동안 객체가 생성된 위치를 확인 하고자 할 때 [Object References 뷰]를 이용한다. 이 뷰는 객체를 생성시킨 메소드를 보여주며 해당 메소드의 소스코드 위치까지 알려준다.

우리는 이 뷰를 [Memory Snapshot]뷰를 통해 연다는 것을 바로 이전 파트에서 배웠다. 이제 문제의 DataGrid 객체가 어디에서 생성되는지 확인하기 위해 이 뷰를 열어보자.



이 뷰는 객체의 리스트를 보여주는 [Instance] 영역과 해당 객체가 생성된 위치를 보여주는 [Allocation Trace] 영역으로 나뉘어져 있다. 위 그림에서 알 수 있듯이 좌측 [Instance] 영역에서 무수히 많이 생성된 DataGrid 객체를 선택하면 우측 [Allocation Trace]에서 그 객체를 생성한 메소드와 소스 위치까지 파악할 수 있다. 이러한 정보를 이용해 잘못된 애플리케이션을 즉시 수정할 수 있을 것이다.


3-1-6. Loitering Objects 뷰
과거 특정한 시점에 메모리 스냅샷 두개를 저장했고 그 두 시점 사이에서 생성 되었던 객체들을 확인하고 싶을 때 [Loitering Objects 뷰]를 이용한다. 이 뷰는 두 시점 사이에서 생성된 객체와, 그 객체가 사용했던 메모리의 총 합계를 보여준다.

이제 이 뷰를 활용하기 위해 프로파일링을 다시 시작하고, 시작 하자마자 메모리 스냅샷을 저장하고 약 10초뒤 다시 한번 메모리 스냅샷을 저장 해보자. 그리고 [Profile 뷰]에서 두 개의 메모리 스냅샷을 선택하고 Find Loitering Objects 버튼을 누르면 다음 그림처럼 [Loitering Objects 뷰]가 나타날 것이다.



위 그림에서 짧은 시간에 DataGrid 객체가 900개나 생성되고 전체 메모리에서 62%에 해당하는 메모리를 사용하고 있음을 알 수 있다. 이 처럼 이 뷰는 메모리 누수를 발견 하는데 유용하며 [Memory Sanapshot 뷰]와 마찬가지로 특정 객체를 더블클릭 하거나 우측 상단의 빨간색으로 마킹된 아이콘을 누르면 해당 객체가 생성된 위치를 보여주는 [Object References 뷰]를 열 수 있다.


3-1-7. Allocation Trace 뷰
[Allocation Trace 뷰]는 바로 이전 파트에서 배웠던 [Loitering Objects 뷰]와 비슷한데 특정한 두 시점 사이의 메모리 상황을 분석할 때, 호출된 메소드에 대한 정보를 알고 싶을 때 이용한다. 앞서 말했듯이 [Loitering Objects 뷰]가 생성 되었던 객체를 보여줬다면, 이 뷰는 호출된 메소드와 그 메소드가 호출되는 동안 생성한 객체의 수를 비롯하여, 사용했던 메모리 양을 보여준다. 

이제 [Profile 뷰]에서 3-1-6 Loitering Object 뷰 파트에서 저장했던 두 개의 메모리 스냅샷을 선택하고, View Allocation Trace 버튼을 누르면 다음 그림처럼 [Allocation Trace 뷰]가 나타날 것이다.



위 그림과 같이 [Allocation Trace 뷰]에는 6개의 컬럼이 존재한다. 이 중 Method, Package 컬럼은 궂이 설명하지 않아도 어떤 컬럼인지 알 수 있을 것이다. 그 두개를 제외한 나머지 컬럼들에 대해 알아보자.

Cumulative Instances
두 시점 사이에서 이 메소드에 의해서 호출된 메소드와 이 메소드 자체에서 생성된 객체의 수이다.

Self Instances
두 시점 사이에서 이 메소드 자체에서 생성된 객체의 수이다.

Cumulative Memory
두 시점 사이에서 이 메소드에 의해서 호출된 메소드와 이 메소드 자체에서 생성된 객체가 사용한 메모리 양이다.

Self Memory
두 시점 사이에서 이 메소드 자체에서 생성된 객체가 사용한 메모리 양이다.


[Allocation Trace 뷰]를 통해서 짧은 시간동안 timerHandler 라는 메소드에서 많은 객체를 생성하고 많은 메모리를 사용한다는 것을 알 수 있다. 위 그림에서 처럼 timerHandler 라는 메소드가 과연 어떠한 객체를 생성했는지는 알 수 없다. 그러나 해당 메소드를 더블클릭 하거나 우측 상단의 빨간색 마킹된 버튼을 누르면 어떤 객체가 생성 됐는지 보여주는 [Object Statistics 뷰]를 열 수 있다. 바로 다음 파트에서 이 뷰에 대해 자세히 알아보겠다.

3-1-8. Object Statistics 뷰
과거 특정한 두 시점 사이에서 호출 되었던 메소드에 대한 메모리 통계정보를 알고 싶을 때 [Object Statistics 뷰]를 이용한다. 이 뷰는 해당 메소드가 수행 되는 동안 생성한 객체가 무엇인지, 그 객체가 얼마나 많은 메모리를 사용했는지 보여준다. 

이 뷰는 바로 이전 파트에서 [Allocation Trace 뷰]를 통해 연다는 것을 배웠다. 이제 특정 메소드의 통계 정보를 보기 위해 이 뷰를 열어보자.



위 그림처럼 이 뷰는 크게 3가지 영역으로 구분할 수 있다.
번 영역은 호출된 메소드에 대한 요약된 정보로서 [Allocation Trace 뷰]에서 보여지던 정보와 동일하다.
번 영역은 이 메소드 자체에서 생성된 객체와 그 객체가 사용하는 메모리 양을 보여준다.
번 영역은 이 메소드에 의해 호출된 다른 메소드에서 생성던 객체와 그 객체가 사용하는 메모리 양을 보여준다.
이 처럼 [Object Statistics 뷰]는 이제껏 봐왔던 뷰들에 대한 전체적인 정보를 보여주는 통계뷰 라고 할 수 있다. 이러한 통계정보를 바탕으로 보다 종합적인 메모리 분석을 할 수 있을 것이다.

우리는 지금 까지 플렉스빌더에서 메모리 프로파일링을 통해 메모리 누수 감지와 원인 발견 및 애플리케이션 전체적인 메모리 상황에 대한 정보를 알 수 있었다. 이런 정보를 안다면 애플리케이션 설계에 있어서도 도움이 될 것이다. 이제 메모리 상황이 아닌 성능에 초점을 둔 퍼포먼스 프로파일링에 대해 알아 보도록 하자.


3-2. 퍼포먼스 프로파일링
퍼포먼스 프로파일링은 애플리케이션에서 응답이 느린 메소드를 찾아내거나, 성능이 향상될 수 있는 메소드를 찾아 내고자 할 때 이용한다. 퍼포먼스 프로파일링은 이 두가지 타입의 메소드를 최적화 하기 위한 리팩토링 정보를 제공하여 애플리케이션의 전체적인 성능을 향상 시킬 수 있다.

우리는 잘못된 예제코드를 이용하여 수행시간 가장 긴 메소드와 빈번히 호출되는 메소드를 찾아낼 것이다. 예제코드는 다음과 같다.



위 예제코드를 다 적었다면 프로파일링을 시작해보자. 이번에는 퍼포먼스 프로파일링만 하기 때문에 실행 옵션을 묻는 창에서 가장 아래 있는 Enable performance profiling 항목에만 체크를 한다. 그리고 나서 애플리케이션이 시작되면 화면에 보이는 버튼을 10회 누른다. 플렉스빌더는 프로파일링이 시작된 이후로 성능 데이터를 수집 하는데, 성능 데이터는 메소드 호출시간과 횟수이다. 따라서 여러분이 버튼을 10회 눌렀다면 버튼에 의해 실행된 메소드 정보가 수집 되었을 것이다. 이제 플렉스빌더로 돌아가서 [Profile 뷰]에 있는 
Capture Performance Profile 아이콘을 누르면, 다음 그림처럼 플렉스빌더가 아이콘을 누른 시점까지 수집했던 성능 데이터를 저장하게 된다.



퍼포먼스 데이터가 저장되면 위 그림처럼 [Profile 뷰]에 목록으로 추가된다. 또한 하나 이상의 퍼포먼스 데이터를 저장할 수 있으며, 플렉스빌더가 수집한 퍼포먼스 데이터를 초기화 하고 싶다면 
Reset Performance Data 아이콘을 누르면 된다. 저장된 목록중 하나를 더블클릭하면 퍼포먼스 데이터를 볼 수 있는 [Performance Profile 뷰]가 열린다. 다음 파트에서 [Performance Profile 뷰]를 통해 수집된 퍼포먼스 데이터를 확인해 보도록 하겠다.

3-2-1. Performance Profile 뷰
[Performance Profile 뷰]는 퍼포먼스 프로파일링을 하는 동안 사용하는 가장 중요한 뷰 이다. 이 뷰는 메소드 호출에 관련된 통계를 보여주며, 이러한 정보는 애플리케이션의 병목현상을 개선하거나 수행속도를 높이는 정보로 이용될 수 있다.

[Profile 뷰]에서 저장된 퍼포먼스 데이터 항목을 더블클릭 하면 다음 그림처럼 [Performance Profile 뷰]가 열릴 것이다.


위 그림과 같이 [Performance Profile 뷰]에는 6개의 컬럼이 존재한다. 이 중 Method, Package 컬럼은 궂이 설명하지 않아도 어떤 컬럼인지 알 수 있을 것이다. 그 두개를 제외한 나머지 컬럼들에 대해 알아보자.

Calls
퍼포먼스 데이터가 저장된 시점까지 메소드가 호출된 횟수이다.

Cumulative Time

이 메소드에 의해서 호출된 메소드들의 총 수행 시간이다.

Self Time
이 메소드 자체의 수행 시간이다.

AVG. Cumulative Time
이 메소드에 의해서 호출된 메소드들의 평균 수행 시간이다.

AVG. Self Time
이 메소드 자체의 평균 수행 시간이다.


위 그림은 Cumulative Time 컬럼을 눌러서 내림차순 정렬한 것 이다. 정렬을 통해서 가장 응답이 느린 메소드를 확인해 본 결과, 자체적인 수행시간이 가장 느린 메소드는 slow() 임을 확인할 수 있다. 이제 여러분이 직접Calls 컬럼으로 정렬을 하면 가장 많이 호출된 메소드는 fast() 임을 확인할 수 있을 것이다. 이제 여러분은 메소드의 수행 시간을 알게 되었으므로 리팩토링을 통해 성능 향상을 꽤할 수 있을 것이다.

또한 이 뷰에서 특정 메소드 항목을 더블클릭 하거나 위 그림의 우측상단 빨간색 마크된 아이콘을 누르면 해당 메소드의 좀 더 진보된 정보를 볼 수 있는 [Method Statistics 뷰]를 열 수 있다. 다음 파트에서 [Method Statistics 뷰]에 대해 자세히 알아 보겠다.

3-2-2. Method Statistics 뷰
퍼포먼스 프로파일링 하는동안 호출된 메소드에 대한 성능 통계정보를 알고 싶을 때 [Method Statistics 뷰]를 이용한다. 이 뷰는 호출된 메소드에 대한 수행시간과 더불어 메소드 내에서 호출된 다른 메소드나, 자신이 어디서 호출되었는지 정보를 제공한다. 

이제 
[Performance Profile 뷰]에서 Cumulative Time 이 가장 오래 걸린 execute() 항목을 더블클릭해서 이 뷰를 열어보자.



이 뷰는 크게 3가지 영역으로 구분할 수 있다.
번 영역은 이 메소드 자체 수행시간으로써 [Performance Profile 뷰]에서 보여지던 정보와 동일하다.
번 영역은 이 메소드를 호출한 메소드에 대한 수행 시간을 보여준다.
번 영역은 이 메소드에 의해 호출된 다른 메소드들의 수행 시간을 보여준다.
이 처럼 [Method Statistics 뷰]는 수행시간과 더불어 메소드들의 흐름과 같은 최적화 하기 유용한 정보를 제공한다. 애플리케이션의 성능과 흐름을 파악하는 것 만으로도 앞으로 여러분이 무엇을 해야할지 알게 될 것이다.


4. 맺음말
지금까지 프로파일링에 대해서 알아봤다. 이 글에서 소개한 방법으로 메모리와 퍼포먼스를 측정하여 여러분의 애플리케이션의 성능을 향상시켜 보도록 하자. 끝으로 프로파일링 또한 디버깅과 마찬가지로 개발이 끝난 후가 아니라 개발 과정의 일부분으로 행해 진다면 좋은 품질의 애플리케이션을 만들 수 있을 것이다.


출처 : http://jjaeko.tistory.com/104

Posted by 1010
00.Flex,Flash,ActionScript2014. 8. 20. 14:26
반응형
참조 : Adobe Flex3 실전 트레이닝 북, 국내 블로그, 해외 블로그
이 글은 어도비리아에 기술문서로 제출 되었으며, 동일한 내용을 어도비리아에서 볼 수 있습니다.



-1부 디버깅-
<목차>
1. 디버깅이란?
2. 디버깅의 종류
2-1. Trace()
2-2. <mx:TraceTarget />
2-3. Flex Debugging perspective
2-3-1. Debug view
2-3-2. Variables view
2-3-3. Expressions view
2-3-4. Breakpoints view
2-4. 
flash.debugger.enterDebugger()
3. 맺음말


1. 디버깅이란?
프로그래머가 의도하지 않은 소프트웨어의 오작동을 버그라고 한다.
디버깅은 이러한 버그를 찾아내고 수정하는 것을 말하며,
소프트웨어의 품질을 향상 시키기위해 반드시 거쳐야 할 과정이다.

이 글에서 여러분은 Flex Builder에서 효과적으로 디버깅 하는 방법을 배울 것이며,
더 나아가 개발 습관에도 좋은 영향을 줄 것이다.


2. 디버깅의 종류
Flex에서 할 수 있는 디버깅은 크게 3가지가 있다.

- Trace()
- <mx:TraceTarget />
- Flex Debugging perspective

이 3가지 방법 모두 "특정한 시점에 객체가 가지고 있는 값" 을 확인하는 것에 초점을 둔다.
그럼으로써 프로그램이 의도한대로 동작하는지 알게 될 것이다.
그리고 이 중에서 Trace()와 <mx:TraceTarget /> 태그는 디버깅 보다는 로깅에 가깝다.
하지만 "특정한 시점에 객체가 가지고 있는 값" 을 확인하는 용도로써 훌륭한 디버깅 도구가 될 수 있다.

그럼 이제부터 이 3가지 종류에 대해 알아보도록 하겠다.

2-1. Trace()

프로그램의 특정 기능이 수행되는 동안 객체들이 의도한 값을 가지고 있는지 확인하고 싶을때 
이 함수를 이용한다. 이 함수는 객체의 값을 콘솔에 출력해주는 역할을 한다.
프로그램의 흐름속에 Trace() 함수를 이용해 값을 확인 함으로써 프로그램이 정상적으로 작동 되는지 
확인할 수 있을 것이다.

간단한 예제를 통해 사용법을 알아 보겠다.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    creationComplete="creationComp()">
    <mx:Script>
        <![CDATA[
            import mx.utils.ObjectUtil;
            private function creationComp():void {
                var obj:Object = new Object();
                obj.a = "a";
                obj.b = "b";
                
                trace(ObjectUtil.toString(obj));
            }
        ]]>
    </mx:Script>
</mx:Application>


위 코드를 디버그 모드로 실행(F11 키 또는 벌레모양 아이콘
) 하면 콘솔에서 다음과 같은 결과를 확인할 수 있다.

(Object)#0
  a = "a"
  b = "b"


위 예제에서 사용한 ObjectUtil 클래스는 Trace() 함수와 함께 사용하면 매우 유용하다.
ObjectUtil.toString() 메서드는 객체가 가지고 있는 프로퍼티와 값을 출력결과 처럼 포맷해서 리턴해준다.

2-2. <mx:TraceTarget />
Flex 애플리케이션을 개발 하는데 있어 어려운 부분 중 하나는 서버와 통신하는 경우이다.
어떤 원인에 의해 서버로부터 기대했던 데이터를 받지 못할 경우 원인을 찾아내기가 힘들다.
이런 경우 <mx:TraceTarget /> 태그를 이용하면 원인을 쉽게 파악할 수 있다.
이 태그는 서버와 통신을 할 때 통신상태 및 전송되는 데이터를 콘솔에 출력해주는 역할을 한다.
단지 태그를 MXML 문서에 추가하기만 하면 콘솔에서 그 내용을 확인할 수 있다.

간단한 예제를 통해 사용법을 알아 보겠다.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">    

    <mx:TraceTarget />
    
    <mx:HTTPService id="http" 
                               url="http://www.naver.com" 
                               resultFormat="text" />
                    
    <mx:Button  label="click me"
                      click="http.send()" />
                                
</mx:Application>


위 코드를 디버그 모드로 실행(F11 키 또는 벌레모양 아이콘) 하면 콘솔에서 다음과 같은 결과를 확인할 수 있다.

mx.messaging.Producer '1D8D6911-2BBB-B851-5B2B-A2BA93C8F10A' producer set destination to 'DefaultHTTP'.
mx.messaging.Channel 'direct_http_channel' channel endpoint set to http:
mx.messaging.Producer '1D8D6911-2BBB-B851-5B2B-A2BA93C8F10A' producer sending message '54738616-39F6-E6DA-940C-A2BA98DFACB2'
mx.messaging.Channel 'direct_http_channel' channel sending message:
(mx.messaging.messages::HTTPRequestMessage)#0
  body = (Object)#1
  clientId = (null)
  contentType = "application/x-www-form-urlencoded"
  destination = "DefaultHTTP"
  headers = (Object)#2
  httpHeaders = (Object)#3
  messageId = "54738616-39F6-E6DA-940C-A2BA98DFACB2"
  method = "GET"
  recordHeaders = false
  timestamp = 0
  timeToLive = 0
  url = "http://www.naver.com"
mx.messaging.Producer '1D8D6911-2BBB-B851-5B2B-A2BA93C8F10A' producer connected.
mx.messaging.Producer '1D8D6911-2BBB-B851-5B2B-A2BA93C8F10A' producer acknowledge of '54738616-39F6-E6DA-940C-A2BA98DFACB2'.
mx.rpc.http.HTTPService Decoding HTTPService response
mx.rpc.http.HTTPService Processing HTTPService response message:
(mx.messaging.messages::AcknowledgeMessage)#0
  body = "결과 값 생략"
  clientId = "DirectHTTPChannel0"
  correlationId = "54738616-39F6-E6DA-940C-A2BA98DFACB2"
  destination = ""
  headers = (Object)#1
    DSStatusCode = 200
  messageId = "EC0ED47D-ABD2-8671-79ED-A2BA9950521B"
  timestamp = 0
  timeToLive = 0


위 결과에서 알 수 있듯이 전송상태 및 전송 데이터("결과 값 생략" 부분)를 쉽게 확인할 수 있다.
또한 이 태그는 디버깅 보다는 로깅과 매우 밀접한 연관이 있다.
하지만 이 글에서는 오직 디버깅의 용도로만 사용한 것이며 로깅에 관한 부분은 다루지 않는다.


2-3. Flex Debugging perspective
Flex Builder(Eclipse Platform, 이하 빌더) 에서 지원하는 디버깅으로써 이 글의 핵심이라고 할 수 있다.
개발자가 원하는 "특정한 시점"에 객체들의 상세한 값을 확인하고자 할 때 이 기능을 이용한다.
빌더에서 지원하는 디버깅 기능을 이용하면 애플리케이션을 일시 중지하고 중지된 상태의 시점에서
객체들의 상태를 확인할 수 있을 뿐만 아니라 한줄한줄 코드 실행을 제어할 수 있다.

그럼 실습을 통해 자세하게 알아보겠다.

빌더에서 지원하는 디버깅은 "특정한 시점" 으로부터 시작된다.
따라서 여러분은 먼저 객체의 값을 확인하고자 하는 "특정한 시점"을 정해야 한다.
먼저 소스뷰에서 특정 라인번호를 더블클릭 하면 다음 그림처럼 빨간색 박스친 부분의 점이 표시될 것이다.
그 점이 바로 "특정한 시점" 이며 애플리케이션이 중지되고 객체의 상태를 확인할 수 있는 지점이다.
빌더에서는 "특정한 시점"을  보다 전문적인 용어로 "브레이크 포인트" 라고 부른다.



이제 애플리케이션을 디버그 모드
(F11 키 또는 벌레모양 아이콘) 로 실행 시켜보자.
애플리케이션이 시작되면 초기화 과정(creationComplete 이벤트)에서 브레이크 포인트 지점의 코드가 
수행된다. 
브레이크 포인트 지점의 코드가 수행될 때 Flex Builder는 자동으로 디버깅 모드로 전환되고
애플리케이션은 정지상태가 된다. 

디버깅 모드로 전환된 모습은 다음 그림과 같다.



①번 영역은 코드 실행을 제어하거나 세부적인 디버깅을 컨트롤할 때 사용하는 Debug view로 구성되어 있다.
②번 영역은 객체의 상세한 정보를 볼 수 있는 Variables view, 
브레이크 포인트의 목록을 확인할 수 있는 Breakpoints view, 
특정 변수를 주시하고 싶을때 사용하는 Expressions view 로 구성되어 있다.

그럼 이제부터 각각의 view에 대해 자세히 알아보겠다.

2-3-1. Debug view
애플리케이션이 브레이크 포인트를 만나서 디버깅 모드로 진입했을 때 애플리케이션을 디버깅을 중지하고 다시 실행시키거나 한줄한줄 코드를 실행하고자 할 때 Debug view를 사용한다.
Debug view에는 다음과 같이 디버깅을 제어할 수 있는 아이콘이 있다.

Resume

브레이크 포인트에 의해 애플리케이션이 정지상태가 되었을 때, 디버깅을 멈추고 정지된 애플리케이션을 이어서 가동시킨다. 만약 또 다른 브레이크 포인트를 만난다면 다시 디버깅 모드로 진입된다.

Suspend

애플리케이션과 함께 디버깅을 일시 중지한다.

Terminate

애플리케이션과 함께 디버깅을 종료한다.

Disconnect

Flex Builder의 디버거와 연결을 끊는다.
연결이 끊기면 브레이크 포인트를 만나더라도 디버깅 모드로 진입할 수 없다.

Step Into

현재 라인이 함수라면 함수 내부의 첫번째 라인으로 이동한다.
만약 현재 라인이 함수가 아니라면 현재 라인을 실행하고 다음 라인으로 이동한다.

Step Over

현재 라인을 실행하고 다음 라인으로 이동한다.

Step Return

현재 실행중인 함수를 호출한 부모함수로 이동한다.
예를들면, Step Into에서 함수 내부로 이동했을 경우 다시 밖으로 빠져 나오게 된다.


2-3-2. Variables view
애플리케이션이 브레이크 포인트를 만나 정지상태가 되었을 경우 정지된 시점에서의 객체들의 값을 확인할 때 Variables view를 이용한다.
다음 그림은 이번 장의 예제코드를 디버그 모드로 실행했을 때의 Variables view의 모습이다.


위 그림에서 this는 현재 브레이크 포인트가 위치한 클래스(또는 MXML문서) 이다.
트리의 노드 앞에 있는 아이콘은 해당 노드의 가시성을 나타낸다.
따라서 this 하위에 있는 a는 private, b는 protected, c는 public 가시성을 가지는 변수이다.
그리고 회색(L) 아이콘의 obj는 현재 수행중인 함수의 로컬변수임을 뜻한다.

this의 트리를 계속 열면 수백 개의 변수를 확인할 수 있으며, Ctrl+F 키나 마우스 우클릭후 Find Variable 메뉴를 통해 다음 그림처럼 특정 변수를 검색할 수 있다.


위 그림처럼 변수명을 입력하면 실시간으로 원하는 변수를 찾을 수 있다.


2-3-3. Expressions view
Variables view의 수많은 변수 중 특정한 변수를 주시하고 싶다면 Expressions view을 이용하면 된다.
Expressions view 는 특정 변수를 주시할 수 있고 변수들의 값을 원하는 형태의 결과로 볼 수 있는 기능을 제공한다.

먼저 예제 코드를 다음과 같이 변경하고 브레이크 포인트를 함수 종료부분에 지정하자.



코드를 변경 했다면 디버그 모드
(F11 키 또는 벌레모양 아이콘) 로 애플리케이션을 시작한다.
애플리케이션이 브레이크 포인트를 만나 디버깅 모드로 진입하면 Variables view에서  주시하고자 하는 변수에
마우스 우클릭후 Create Watch Expression 메뉴를 클릭한다. 
이제 Expressions view를 확인해보면 다음 그림처럼 선택한 변수가 추가되어 있을 것이다.


이렇게 Expressions view에 변수를 추가해 놓으면 디버깅 하는 동안 Variables 창의 수많은 변수들 중 해당 변수를 찾을 필요 없이 바로 확인할 수 있다.

이번에는 Expressions view를 보다 효과적으로 사용하는 방법에 대해 알아보겠다.
먼저 Expressions 
view에서 마우스 우클릭후 Add Watch Expression 메뉴를 클릭한다.
그리고 다음 그림처럼 변수들을 사칙연산으로 표현해보자.



OK 버튼을 누르고 Expressions 
view를 확인해보면 다음 그림처럼 보일 것이다.



이처럼 Expressions 
view에서는 특정 변수를 주시할 수 있을 뿐만 아니라 변수들의 연산까지 지원해서
변수들의 상호작용에 대한 결과를 바로 알 수 있다.

2-3-4. Breakpoints view
브레이크 포인트를 여기저기 지정하고 해당 문서를 닫았을 경우 지정한 브레이크 포인트들을 찾는 것은 쉬운일이 아니다. 이럴 때 Breakpoints view를 사용하면 지정한 브레이크 포인트들을 쉽게 찾을 수 있다.

다음 그림을 살펴보자.


이처럼 Breakpoints view에는 지정한 브레이크 포인트들의 리스트가 보여진다.
그리고 Delete키 또는 마우스 우클릭후 Remove메뉴를 통해 브레이크 포인트를 바로 제거할 수 있다.

2-4. flash.debugger.enterDebugger()
브레이크 포인트는 빌더에서 관리되기 때문에 브레이크 포인트 자체를 소스코드와 함께 공유할 수는 없다.
그러나 브레이크 포인트와 똑같은 기능을 수행하는 함수가 있다.
바로 flash.debugger.enterDebugger() 함수이다.
이 함수는 브레이크 포인트와 똑같은 기능을 수행하기 때문에 소스코드와 함께 디버깅 지점을 공유할 수 있다.

다음 그림을 보자.


디버깅 지점을 소스코드로 명시했기 때문에 Breakpoints view에 나타나지 않는다.
하지만 그 외에 브레이크 포인트와 다른 점은 없다.


3. 맺음말
지금까지 디버깅에 대해서 알아봤다.
앞으로는 이 글에서 소개한 디버깅 방법으로 소프트웨어 품질을 향상시키는데 노력하자.
마지막으로 디버깅은 개발이 끝난 이후에 하는 것이 아니라 개발과정의 일부분 이라는 것을 명심해야 한다.


출처 : http://jjaeko.tistory.com/103

Posted by 1010
03.HTML 4.X, HTML5, XML...2014. 8. 20. 13:53
반응형

출처 : http://bryan7.tistory.com/202


IE Memory Leak – jQuery Fix


http://kossovsky.net/index.php/2009/07/ie-memory-leak-jquery-garbage-collector/


댓글에 보니 jQuery 를 수정해서 문제를 해결했다는 얘기도 있긴 했지만, 내가 해 봤을 때는 별 효과가 없었다.


IE memory leak


http://epro.tistory.com/6


1. 원인은?

Understanding and Solving Internet Explorer Leak Patterns를 참고해 보면 순환참조 부분이 가장 의심이 되었다.
Ajax를 편하게 쓰기 위해 XMLHttpRequest를 생성해주는 function을 만들어 두고 필요할때 불러서 쓰곤 했는데, 이 부분에서 문제가 있는 듯 했다.
local variable은 function이 종결되면 가비지컬렉터에 의해 삭제되어야 하는데, parameter로 다른 function에 참조되어 있을 경우- 이런 경우를 순환참조라고 하는 것 같다 - 는 leak의 원인이 된다고 한다. (어설픈 해석이다. 대충 이런 뜻인 것 같다)

.... It isn't immediately obvious that parent function parameters and local variables will be frozen in time, referenced, and held until the closure itself is released. .... Because we've added a closure, a second reference is made, and that second reference won't be released until the closure is also released. .....

정확한 내용은 Reference의 Closures부분을 참조할 것
그래서 지역변수로 선언된 XMLHttpRequest를 전역변수로 빼고 비교해 보기로 했다.


Screencast: Diagnosing JavaScript Memory Leaks in IE


http://www.barelyfitz.com/screencast/javascript/memory-leak/


[스크립트] Script 메모리 누수에 대한 TIP


http://www.phpschool.com/gnuboard4/bbs/board.php?bo_table=tipntech&wr_id=71441


JavaScript and memory leaks


Memory leaks

http://javascript.info/tutorial/memory-leaks


Understanding and Solving Internet Explorer Leak Patterns

http://msdn.microsoft.com/en-us/library/ms976398.aspx


IE9 이하에서 (IE9 포함) Memory Leak 이 발생하는 경우 -


[2013-12-24]

5초마다 한번씩 반복적으로 ajax로 호출을 하니까 chrome 브라우저에서는 괜찮은데, IE 8, 9 에서는 메모리 증가 현상이 뚜렷했다. 저녁에 웹페이지를 켜 놓은 상태에서 퇴근하고, 아침에 출근해 보면 IE가 먹통이 되었다.

나는 처음에는 ajax 의 반복 호출로 인해 메모리가 조금씩 쌓였을 것이라고 생각했는데, ajax 에서 아무 일도 안 한 상태로 반복 호출해보니 메모리 증가 현상이 없었다. 


ajax 로 서버에서 리스트를 받아서 웹페이지에서 table 태그를 지우고, 다시 써 넣는 작업을 하고 있다.


원인은 <a> 태그 안에 onclick='...' 으로 문자열로 넣은 부분 때문이었다.

IE 9 버전 이하에서는 웹브라우저의 Garbage Collector 의 버그 때문에 이런 경우 onclick 안의 function 의 참조 때문에 화면에서 지워진 태그가 Garbage Collect 되지 않는 것 같다.


Chrome Browser 와 IE 11 브라우저에서는 메모리 증가 현상이 없었다.


해결책은 다음 코드와 같이 onclick='...' 으로 문자열을 적어 넣지 말고, jquery의 bind() 메서드를 사용하면 된다.


  1. // [2013-12-24] Heeseok
  2. // 다음과 같이 하면 IE 9 이하에서 Memory Leak 이 발생하므로 bind 메서드를 써준다.
  3. //td.html("<a href='javascript:" + fnGoDetail + "()' onclick='" + fnGoDetail + "(this);return false;'>" + list[r][col] + "</a>");
  4. td.html("<a href='javascript:" + fnGoDetail + "()'>" + list[r][col] + "</a>");
  5. td.find('a').bind("click", function() {
  6.     eval(fnGoDetail + "(this)");
  7.     return false;
  8. });


IE Process의 Memory 를 30초 단위로 파일로 저장하는 배치 파일


@echo off

SET count=0

SET mem=tasklist /fi ^"imagename eq iexplore.exe^" /NH

echo --------------------------------------------------------------------------- >> memory_iexplore.log

:start

SET /A count+=1

echo %date% %time% (%count%) >> memory_iexplore.log

%mem% >> memory_iexplore.log

echo --------------------------------------------------------------------------- >> memory_iexplore.log

TIMEOUT 30

goto start


배치 파일명: memory_iexplore.cmd
저장하는 로그 파일명: memory_chrome.log
파일에 저장되는 내용:

--------------------------------------------------------------------------- 
2013-12-24 19:08:11.08 (1) 

iexplore.exe                  1836 Console                    1     27,908 K
iexplore.exe                  6764 Console                    1    175,692 K
--------------------------------------------------------------------------- 
2013-12-24 19:08:41.15 (2) 

iexplore.exe                  1836 Console                    1     27,908 K
iexplore.exe                  6764 Console                    1    178,324 K
--------------------------------------------------------------------------- 

실행되는 모습:


IE Process의 메모리가 증가하는지 쉽게 확인하는 법


Process Explorer 에서 iexplore.exe 프로세스를 찾아서 마우스로 오른쪽 클릭하고, Properties... 메뉴 선택.

Performance Graph 탭을 보면 IE Process만 사용하는 CPU, Private Bytes, I/O 를 볼 수 있다.



Memory Leak 확인 예제 #1

테스트한 브라우저: IE9

테스트 결과: 어느 정도 급격하게 증가한 이후에는 증가하지 않는다.

29.6 MB => 220.2 MB => 411.8 MB => 411.8 MB => 411.9 MB


  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <script type="text/javascript">
  5.     function LeakMemory(){
  6.         var parentDiv = document.createElement("div");
  7.         parentDiv.onclick=function() {
  8.           foo();
  9.         };

  10.         parentDiv.bigString =
  11.           new Array(1000).join(new Array(20000).join("XXXXX"));

  12.         alert('Leak Memory Done');
  13.     }

  14. </script>
  15. </head>
  16. <body>
  17. <input type="button" value="Memory Leaking Insert" onclick="LeakMemory()" />
  18. </body>
  19. </html>



Memory Leak 확인 예제 #2

테스트한 브라우저: IE9

테스트 결과: 어느 정도 메모리가 증가한 이후로는 더 이상 증가하지 않았다. (메모리가 많이 증가했다가 alert 버튼을 누르고 나면 다시 원상태로 돌아옴.)

28.1 MB => 39.0 MB => 36.4 MB => 38.4 MB => 37.6 MB => 37.5 MB


  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <script type="text/javascript">
  5.     function leak() {
  6.       var e = document.createElement('div');
  7.       var x = { elementReference: e };
  8.       e.jsReference = x;
  9.     }

  10.     function test() {
  11.       for (var i = 0; i < 1000000; i++)
  12.         leak();
  13.       alert('Done');
  14.     }
  15. </script>
  16. </head>
  17. <body>
  18. <input type="button" value="Circular Reference" onclick="test()" />
  19. </body>
  20. </html>


Posted by 1010
00.Flex,Flash,ActionScript2014. 8. 20. 13:49
반응형

[FLEX] Memory leak 문제

Flex Memory Leak 해결 방법

1. addEventListener로 등록된 모든 이벤트들을 removeEventListener로 삭제한다.
- target.addEventListener(eventName,returnFuntion);
- target.removeEventListener(eventName,returnFuntion);

2. 객체타입 멤버 속성들을 null로 셋팅한다.
- 로컬 변수들은 자동으로 소멸되기 때문에 별다른 신경을 쓸 필요가 없지만 멤버 변수로 정의된 객체 타입의 변수들은
인스턴스가 소멸될때 반드시 null로 처리 해야한다.

public class BarGaugeGrid extends ContentBox
{
//멤버 변수
private var _barCount:int = 5;
private var _dataProvider:ArrayCollection;
private var textField:Label;
private var valueLabelField:Label;
private var itemValues:Array = [];
private var dataChanged:Boolean = false;
public function BarGaugeGrid()
{
super();
layout = "vertical";
setStyle("verticalGap", 0);
}

...
public function destroy():void
{
_dataProvider = null;
textField = null;
valueLabelField = null;
itemValues = null;
}



위 2가지만 지켜진다면 대부분의 Memory Leak문제들은 해결된다.


출처 : http://m.truesolution.co.kr/view.php?UID=2613

Posted by 1010
반응형

데이터 유형 설명

프리미티브 데이터 유형에는 Boolean, int, Null, Number, String, uint 및 void 등이 포함됩니다. 또한 ActionScript 기본 클래스에서 Object, Array, Date, Error, Function, RegExp, XML 및 XMLList 등의 복합 데이터 유형을 정의합니다.

Boolean 데이터 유형

Boolean 데이터 유형은 true와 false 두 가지 값으로 구성됩니다. 그 외 다른 값은 Boolean 유형의 변수에 사용할 수 없습니다. 선언되었지만 초기화되지 않은 부울 변수의 기본값은 false입니다.

int 데이터 유형

int 데이터 유형은 내부적으로 32비트의 정수로 저장되며

-2,147,483,648 (-231)부터 2,147,483,647(231 - 1)까지의 정수 집합(-2,147,483,648 및 2,147,483,647 포함)으로 구성됩니다. 이전 버전의 ActionScript에서는 정수와 부동 소수점 숫자에 모두 사용되는 Number 데이터 유형만 제공했습니다. ActionScript 3.0에서는 이제 32비트의 부호가 있거나 없는 정수에 대한 저수준의 시스템 유형에 액세스할 수 있습니다. 변수에서 부동 소수점 숫자를 사용하지 않는 경우 Number 데이터 유형 대신 int 데이터 유형을 사용하는 것이 보다 빠르고 효율적입니다.

최소 및 최대 int 값의 범위를 벗어나는 정수 값인 경우 Number 데이터 유형을 사용하여 양수와 음수 각각 9,007,199,254,740,992(53비트 정수 값) 사이의 값을 처리할 수 있습니다. int 데이터 유형의 변수에 대한 기본값은 0입니다.

Null 데이터 유형

Null 데이터 유형에는 null 값만 포함됩니다. 이는 Object 클래스를 포함하여 복합 데이터 유형을 정의하는 모든 클래스와 String 데이터 유형의 기본값입니다. Boolean, Number, int 및 uint 같은 기타 프리미티브 데이터 유형에는 null 값이 포함되어 있지 않습니다. Boolean, Number, int 또는 uint 유형의 변수에 null을 지정하려고 하면 Flash Player 및 Adobe AIR에서 null 값을 적절한 기본값으로 변환합니다. 이 데이터 유형은 유형 약어로 사용할 수 없습니다.

Number 데이터 유형

ActionScript 3.0에서 Number 데이터 유형은 정수, 부호 없는 정수, 부동 소수점 숫자 등을 나타낼 수 있습니다. 그러나 성능을 최대화하려면 32비트int 및 uint 유형이 저장할 수 있는 값보다 큰 정수 값 또는 부동 소수점 숫자에만 Number 데이터 유형을 사용해야 합니다. 부동 소수점 숫자를 저장하려면 숫자 안에 소수점을 넣습니다. 소수점을 생략하면 숫자가 정수로 저장됩니다.

Number 데이터 유형에서는 이진 부동 소수점 산술에 대한 IEEE 표준(IEEE-754)에 지정된 64비트 배정밀도 형식을 사용합니다. 이 표준에는 64비트를 사용하여 부동 소수점 숫자를 저장하는 방법이 규정되어 있습니다. 1비트는 숫자가 양수 또는 음수인지 지정하는 데 사용됩니다. 11비트는 2를 밑수로 하는 지수를 저장하는 데 사용됩니다. 남은 52비트는 지수에 따라 거듭제곱되는 숫자인 유효 숫자(또는 가수)를 저장하는 데 사용됩니다.

Number 데이터 유형에서는 지수를 저장하는 데 일부 비트를 사용하므로 모든 비트가 유효 숫자를 저장하는 데 사용되는 경우에 비해 매우 큰 부동 소수점 숫자를 저장할 수 있습니다. 예를 들어, Number 데이터 유형에서 유효 숫자를 저장하는 데 64비트를 모두 사용하는 경우, 저장할 수 있는 가장 큰 숫자는 265 - 1입니다. 11비트를 사용하여 지수를 저장하면 Number 데이터 유형에서 유효 숫자를 21023으로 거듭제곱할 수 있습니다.

Number 유형에서 나타낼 수 있는 최소 및 최대 값은 Number.MAX_VALUE 및 Number.MIN_VALUE라는 Number 클래스의 정적 속성에 저장됩니다.

Number.MAX_VALUE == 1.79769313486231e+308 
Number.MIN_VALUE == 4.940656458412467e-324

Number 데이터 유형의 경우 숫자의 범위는 방대하지만 정밀도는 떨어집니다. Number 데이터 유형에서는 유효 숫자를 저장하는 데 52비트를 사용하므로 정확하게 나타내기 위해 52비트 이상이 필요한 숫자(예: 분수 1/3)는 근삿값만 표현할 수 있습니다. 응용 프로그램에서 절대 정밀도의 십진수가 필요한 경우 이진 부동 소수점 산술과 대립되는 십진 부동 소수점 산술을 구현하는 소프트웨어를 사용해야 합니다.

Number 데이터 유형을 사용하여 정수 값을 저장하면 유효 숫자의 52비트만 사용됩니다. Number 데이터 유형에서는 해당 52비트 및 특수 은폐 비트를 사용하여 -9,007,199,254,740,992(-253)부터 9,007,199,254,740,992(253)까지의 정수를 나타냅니다.

Flash Player 및 Adobe AIR에서는 NaN 값을 Number 유형의 변수에 대한 기본값으로 사용할 뿐만 아니라 숫자를 반환해야 하지만 이를 반환하지 않는 모든 연산의 결과로도 사용합니다. 예를 들어, 음수의 제곱근을 계산하려고 하면 결과는 NaN이 됩니다. 기타 특수 Number 값에 양의 무한대와 음의 무한대가 포함됩니다.

참고: 제수가 0인 경우 0으로 나눈 결과도 NaN입니다. 피제수가 양수인 경우 0으로 나누면 무한대가 되고, 피제수가 음수인 경우 0으로 나누면 음의 무한대가 됩니다.

String 데이터 유형

String 데이터 유형은 16비트 문자열을 나타냅니다. 문자열은 내부적으로 UTF-16 포맷을 사용하여 유니코드 문자로 저장합니다. 문자열은 Java 프로그래밍 언어와 마찬가지로 변경할 수 없는 값입니다. String 값에 대한 작업은 해당 문자열의 새 인스턴스를 반환합니다. String 데이터 유형으로 선언되는 변수의 기본값은 null입니다. null 값과 빈 문자열("") 모두 문자가 없다는 것을 나타내지만 서로 다릅니다.

uint 데이터 유형

uint 데이터 유형은 내부적으로 32비트의 부호 없는 정수로 저장되며 0부터 4,294,967,295(232 - 1)까지의 정수 집합(0 및 4,294,967,295 포함)으로 구성됩니다. 음이 아닌 정수를 호출하는 특수한 경우에는 uint 데이터 유형을 사용합니다. 예를 들어 int 데이터 유형에는 색상 값 처리에 적합하지 않은 내부 부호 비트가 포함되어 있기 때문에 픽셀의 색상 값을 나타내려면 uint 데이터 유형을 사용해야 합니다. 최대 uint 값보다 큰 정수 값의 경우에는 53비트 정수 값을 처리할 수 있는 Number 데이터 유형을 사용합니다. uint 데이터 유형의 변수에 대한 기본값은 0입니다.

Void 데이터 유형

void 데이터 유형에는 undefined 값만 포함됩니다. 이전 버전의 ActionScript에서 Object 클래스의 인스턴스에 대한 기본값으로 undefined를 사용했습니다. ActionScript 3.0에서 Object 인스턴스에 대한 기본값은 null입니다. Object 클래스의 인스턴스에 undefined 값을 지정하려고 하면 Flash Player 또는 Adobe AIR에서 이 값을 null로 변환합니다. undefined 값은 유형이 지정되지 않은 변수에만 지정할 수 있습니다. 유형이 지정되지 않은 변수는 유형 약어가 없거나 유형 약어에 별표(*) 기호를 사용하는 변수입니다. void는 반환 유형 약어로만 사용할 수 있습니다.

Object 데이터 유형

Object 데이터 유형은 Object 클래스에 의해 정의됩니다. Object 클래스는 ActionScript에서 모든 클래스 정의에 대한 기본 클래스 역할을 합니다. ActionScript 3.0 버전의 Object 데이터 유형은 세 가지 면에서 이전 버전의 Object 데이터 유형과 다릅니다. 첫 번째, Object 데이터 유형은 더 이상 유형 약어가 없는 변수에 지정되는 기본 데이터 유형이 아닙니다. 두 번째, Object 데이터 유형에는 Object 인스턴스의 기본값이었던 undefined 값이 더 이상 포함되지 않습니다. 세 번째, ActionScript 3.0에서는 Object 클래스의 인스턴스에 대한 기본값이 null입니다.

이전 버전의 ActionScript에서 유형 약어가 없는 변수는 자동으로 Object 데이터 유형으로 지정되었습니다. ActionScript 3.0에는 유형이 지정되지 않은 변수라는 개념이 도입되면서 이전 버전과는 다르게 처리됩니다. 이제 유형 약어가 없는 변수는 유형이 지정되지 않은 변수로 간주됩니다. 변수의 유형을 지정하지 않으려는 자신의 의도를 코드를 읽는 사람에게 분명히 밝히려면 유형 약어로 새 별표(*) 기호를 사용할 수 있습니다. 이는 유형 약어를 생략하는 것과 같습니다. 다음 예제에서는 유형이 지정되지 않은 변수 x를 선언하는 동일한 두 명령문을 보여 줍니다.

var x 
var x:*

유형이 지정되지 않은 변수에만 undefined 값을 저장할 수 있습니다. 데이터 유형이 있는 변수에 undefined 값을 지정하려고 하면 Flash Player 또는 Adobe AIR에서 undefined 값을 해당 데이터 유형의 기본값으로 변환합니다. Object 데이터 유형의 인스턴스인 경우 기본값은 null이며, 이는 Object 인스턴스에 undefined를 지정하려고 하면 Flash Player 또는 Adobe AIR에서 undefined 값을 null로 변환한다는 것을 의미합니다.


Posted by 1010
00.Flex,Flash,ActionScript2014. 7. 30. 15:18
반응형

Apache FlexJS es un nuevo framework creado dentro del proyecto Apache Flex. El Objetivo detrás de este esfuerzo es permitir a los desarrolladores crear aplicaciones escritas en MXML y ActionScript y desplegarlas en todo tipo de entornos, tanto con Flash Player, como sin este, con HTML/JS/CSS.

FlexJS puede generar aplicaciones HTML y funcionar en cualquier navegador sin Flash Player. También puede obtener aplicaciones SWF

FlexJS está basado en el concepto de “frameworks paralelos”, donde los bloques y piezas de construcción existirán tanto en AS com en JS. El compilador (basado en Falcon, la evolución de última generación del compilador de AS3 y MXML), es el encargado de traducir MXML y AS a JS y obtener salidas tanto en SWF como en JS. Es en este último caso donde eliminamos la necesidad del Flash Player pudiendo correr la aplicación en cualquier tipo de navegador.

El siguiente diagrama describe la arquitectura sobre la cual se basa el diseño de FlexJS:

Apache FlexJS Diagrama de Cajas

Apache FlexJS Diagrama de Cajas

Compatibilidad con el SDK actual

El SDK actual tiene mucho código fruto de su evolución a lo largo de los años (tengamos en cuenta que el framework tiene ya alrededor de 10 años). Por tanto la dirección que se ha tomado es hacer una base de código desde cero. Esto en la práctica significa que las aplicaciones actuales creadas con versiones del SDK actual no podrán ser compiladas con FlexJS. No obstante, la reescritura de estas aplicaciones tenderá a ser más sencilla en FlexJS dada la similitud que con otra tecnología.

De hecho, el nuevo framework tiene que funcionar bajo un subconjunto de funcionalidades ya que la tecnología de los navegadores y Javascript todavía no es equiparable a las capacidades de Flash Player. Es por tanto evidente, que para conseguir aplicaciones HTML/JS/CSS estás tienen que estar pensadas bajo las restricciones de este entorno.

Ventajas

A parte del acceso a dispositivos donde Flash Player nunca estará presente (principalmente los navegadores iOS), las ventajas para el desarrollado de aplicaciones son claras. Usar el conjunto de herramientas y lenguajes altamente tipados y con orientación a objetos que son fundamentales para todas aquellas aplicaciones de tamaño y funcionalidad medio alto. Se trata pues de recuperar muchas de las características perdidas cuando pasamos del mundo Flash/Flex a un mundo puramente Javascript.

Demo

El ejemplo inicial creado por Alex Harui pone de manifiesto la potencia que puede tener este nuevo framework si observamos las versiones flash y html. La captura siguiente ofrece una visión de cada una de las versiones compiladas con FlexJS (SWF y JS):

Flex JS Demo Inicial

Flex JS Demo Inicial

En el ejemplo se recuperan valores de bolsa de una fuente de datos remota y nos permite pedír el precio. La versión SWF se encuentra aquí y la versión HTML/JS aquí. El código fuente se puede ver seleccionando la opción View Source del menú conextual en la versión Flash.

Estado

FlexJS está en desarrollo por un grupo pequeño pero constante de desarrolladores de Apache Flex encabezado por Alex Harui (desarrollador del SDK desde los comienzos de Flex en Macromedia, actual PMC Chair del proyecto y uno de los principales soportes para el resto de usuarios de Flex por sus contribuciones en la lista de flexcoders). Los puntos acordados para adquirir el estado alpha son:

  • Integración del compilador con Flash Builder
  • Soporte para la creación de Aplicación y Vistas
  • Soporte de propiedades CSS personalizadas
  • Implementación inicial del componente DataGrid
  • Implementación inicial del componente Chars

Algunos de estos puntos ya están conseguidos o cerca de ser completados por lo que es de esperar que la fecha de salida de la versión alpha del producto esté cercana.

Desde el proyecto se pide ayuda de la comunidad para avanzar de forma más rápida. El modelo de Apache se basa en voluntariado y por tanto el avance de FlexJS está directamente relacionado con el soporte de toda la comunidad.

Enlaces

Para leer más sobre FlexJS os proporciono los siguientes enlaces de forma que podáis investigar más sobre este prometedor framework:

 Wiki sobre FlexJS en Apache Flex
 Charla sobre FlexJS ofrecida por Alex Harui por Adobe Connect
 Using FlexJS with Adobe Flash Builder
 Apache Flex Jira
 Lista de correo en Apache Flex (mucho tráfico, para leer sobre FlexJS filtrar por el prefijo “[FlexJS]“)

Conclusión

Hablaremos mucho de FlexJS en Made In Flex, ya que ofrece no solo un futuro prometedor para los desarrolladores Flex que no quieren perder la rapidez y solvencia de sus entornos y lenguajes, para volver a las incomodidades de la programación JS, si no que además aporta verdadera innovación en la conceptualización del propio framework.

Lo veremos con la capacidad de FlexJS para añadir distintos juegos de componentes (html, jquery, createjs,…), la reducción extrema del peso y tamaño de la aplicación generada -tanto en SWF como en JS- o los nuevos conceptos de Strands y Beads, que permiten promocionar la composición sobre la herencia, eliminando la existencia de god classes(como UIComponent con 15.000 lineas de código en el SDK actual).

De forma rápida, en FlexJS un Bead es una funcionalidad que se puede añadir o quitar de un componente como un verdadero plugin, de forma que podemos usarla cuando realmente los necesitemos en la más pura filosofía “pay-as-you-go” (ej: Añadir funcionalidades de touch en dispositivos móviles, mientras que en navegadores y escritorio tenemos funcionalidades de roll over).

Volvemos en breve para ver más temas sobre Apache FlexJS.

¡Comparte este artículo si te ha parecido interesante!

Posted by 1010
02.Oracle2014. 7. 29. 17:57
반응형

Oracle JDBC의 CachedRowSet Implement Bug

CachedRowSet을 사용하는 경우 Oracle JDBC의 CachedRowSet Implementation Bug 때문에 더 이상 고생하는 개발자가 없기를 바라며 이 글을 작성한다.

결론부터 말하자면 Oracle의 JDBC Driver가 CachedRowSet을 Implement한 Class의 Method 중 Date Type에 대한 Bug로 인해 CachedRowSet을 이용한 Code에서 오류가 발생될 수 있다는 것이다. 그리고 이는 Patch된 최신 Version인(현재 시점) 'ojdbc5.jar' 이상을 사용해야 해결될 수 있는 문제라는 것이다.

아래의 몇 몇 오류들은 Bug가 있는 - 오류가 Patch되지 않은 - Oracle JDBC를 이용해 CachedRowSet을 사용할 경우 발생될 수 있는 Error Message들의 예로, 궁극적인 문제의 원인은 Date Type의 DB Field를 CachedRowSet의 'getDate()' Method로 참조할 때 발생하는 'java.lang.ClassCastException'이 바로 그 것이다.
 
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.transaction.TransactionSystemException: Could not roll back JDBC transaction; nested exception is java.sql.SQLException: Connection is closed.
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:583)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:501)
javax.servlet.http.HttpServlet.service(HttpServlet.java:627)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)

[ErrorMessage-1]
  • ErrorMessage-1
       Spring Framework를 연동했을 때 발생될 수 있는 예로 'ClassCastException' 발생으로 인해 해당 Transaction에 대한 Roll back을 Spring Framework이 AOP를 통해 수행하려는 상황에서 발생된 오류인데, 이 오류는 엄밀히 말하면 일반적인 상황에서는 발생하지 않을 수도 있다. 왜냐하면 이 상황은 필자의 경우로 'Javassist'를 이용해 Connection Object를 Intercept한 경우이기 때문이다.

org.springframework.jdbc.UncategorizedSQLException: SqlMapClient operation; uncategorized SQLException for SQL []; SQL state [null]; error code [0];
--- The error occurred in sqlMap.xml.
--- The error occurred while applying a result map.
--- Check the scope.select.
--- Check the result mapping for the 'createDate' property.
--- Cause: java.lang.ClassCastException: java.sql.Timestamp; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException:
--- The error occurred in sqlMap.xml.
--- The error occurred while applying a result map.
--- Check the scope.select.
--- Check the result mapping for the 'createDate' property.  
--- Cause: java.lang.ClassCastException: java.sql.Timestamp
[ErrorMessage-2]

  • ErrorMessage-2
       Spring Framework와(과) iBATIS를 연동했을 경우 debugging 과정에서 추출한 Error Message로 오류의 원인을 정확히 짚어내고 있다. 하지만 오류내용을 보면 마치 'java.sql.Timestamp' Type을 다른 Type으로 잘 못 Casting해 발생한 오류인 것 처럼 보인다.
사실과 다르게 말이다. Error Message에 보이는 'createDate'라는 Field는 분명 'Date' Type인 것이다. 또한 CachedRowSet이 아닌 ResultSet으로는 전혀 문제가 발생하지 않은 Code이다.

이 문제의 원인을 찾는데 상당히 많은 시간을 소요했으며 정신적 스트레스 또한 말 할 수 없이 많았다. 지금까지의 Posting 글이 그러했듯 같은 오류에 대해 적어도 필자 보다는 적은 시간과 노력으로 좋을 결과를 얻기를 바라는 마음이다.
국내 검색 Site에서는 관련 오류 내용을 검색조차 할 수 없었으나 역시 Googling을 통해 알게된 외국의 한 Community Site에 올린 누군가의 글로 해결의 실마리를 찾게 되었다. 물론 이 도 쉽지많은 않앗지만 말이다. 아무튼 도움이 되길 바라며 Posting한다.


출처 : http://mobicator.blogspot.kr/2010/04/oracle-jdbc-cachedrowset-implement-bug.html

Posted by 1010
반응형
mx.utils 에는 편하게 사용할 수 있는 api 가 담겨있다. 

ObjectUtil.toString 같은 것은 많이 사용하고 꽤나 유용하게 쓰인다. 

이 포스트에서 이야기 할 substitute 는 간단하게 이야기 하면 문자열을 치환 해주는 메서드이다.

간단히 코드를 보면 이해가 될것이다. 

1private var str:String "select * from TABLE_NAME where userid='{0}' and menuid='{1}'";
2 
3private function build():void
4{
5    var query:String = StringUtil.substitute(str,"rinn","n002");
6    trace(query);
7    //select * from TABLE_NAME where userid='rinn' and menuid='n002'
8}


위코드에서 보듯이 String 값에서 {n} 형식으로 중괄호로 묶여진 것들을 찾아서. 
거기에 파라미터로 받은 값을 넣어준다. 
n 값에 따라서 파라미터가 순서대로 들어가게 된다. 

{0} 에는 첫번째가 {1}에는 두번째가 들어가고 문자열 안에서 등장하는 순서는 상관이 없다.

1private var str2:String "select * from TABLE_NAME where userid='{0}' and menuid=(select menuno from TABLE2 where userid='{0}' and menuid='{1}')";
2 
3private function build2():void
4{
5    var query:String = StringUtil.substitute(str2,"rinn","n002");
6    trace(query);
7    //select * from TABLE_NAME where userid='rinn' and menuid=(select menuno from TABLE2 where userid='rinn' and menuid='n002')
8}


{0} 이 두번 나오면 두번다 파라미터 첫번째 값이 들어가게 되는 것이다. 

이 방법의 좋은점은 대상이 되는 문자열을 파일로 저장해놓을 수 있다는 점이다. 

기본 쿼리 같은 경우 파일로 저장해놓고 불러다가 치환해서 사용하게 되면.. 테이블 이름정도가 바뀐다거나 하는 것들은 간단하게 파일만 수정하는 것으로도 해결이 된다. 

메뉴의 링크 정보를 저장하는 경우에도 편리하다. 권한 정보 같은것에 따라 파라미터가 바뀐다거나 하는 경우에.. 기본 링크 정보를 저장해놓으면 이후에 간단한 것은 컴파일 할 필요없이 수정해 줄수 있다. 

사실 쿼리 같은 경우 iBatis 를 쓰는 것이 훨씬 간편하고 다이나믹 하게 쿼리를 생성할 수 있지만. 간단한 httpService 를 사용하는 경우에는 유용할 수 있겠다. 

substitute 함수는 간단하게 구현되어있다. 

01public static function substitute(str:String, ... rest):String
02{
03    if (str == nullreturn '';
04     
05    // Replace all of the parameters in the msg string.
06    var len:uint = rest.length;
07    var args:Array;
08    if (len == 1 && rest[0is Array)
09    {
10        args = rest[0as Array;
11        len = args.length;
12    }
13    else
14    {
15        args = rest;
16    }
17     
18    for (var i:int 0; i < len; i++)
19    {
20        str = str.replace(new RegExp("\\{"+i+"\\}""g"), args[i]);
21    }
22 
23    return str;
24}


간단하게 받아온 파라미터를 Array로 받고 for문 돌면서 정규식으로 찾아서 바꿔주는 구조이다.
사실 이런것은 필요한 경우에 그냥 만들어서 쓰게 되는데 이미 api 에서 제공하는 것을 귀찮게 만들어 쓸 필요가 있는가!!
그냥 있는 걸 쓰자.. -ㅅ-;

mx.utils 패키지 같은 경우에 간단 간단한 함수로 되어있기 때문에 api 소스를 보는 것 만으로도 초보 개발자에게는 공부가 된다. 결정적으로 그리 어렵지 않다.. 

시간날때 한번씩 뭐가 있는지 봐놓으면.. 나중에 뻔히 제공되는 메서드를 놔두고.. 개발하는 사태는 벌어지지 않을것이다.. 
우리는.. 그거 말고도 할일이 많지 않은가.. ㅠㅠ


아.. 주말도 출근 해야 하나.....

출처 : http://rinn.kr/31


Posted by 1010
02.Oracle/DataBase2014. 7. 23. 15:08
반응형

내가 지식이 얕은 건지, 어떻게든 돌아가게는 만든다는 주먹구구식 마인드 때문인지,
Oracle DB에 입력되어 있는 순차적인 데이터의 선택적 추출은
이 방법밖에 알지 못한다..

자, 이제 그 방법에 대해 알아보자.

먼저 MySQL 에서는 
# [MySQL]
SELECT a.love_id 
FROM love a
LIMIT 1000, 2000;


하면, 실제 입력된 데이터의 순서에 맞추어 1000번 부터 2000번까지의 데이터가 나온다.

하지만.... 망할 ORACLE 에는 없다는 거....

어떤 분들은 'rownum으로  WHERE rownum BETWEEN 1000 AND 2000 하면 되지 않느냐??'
라고 하는데..... 잘 찾아보면, rownum 은 결과 값에 매겨지는 상대적인 값이므로,
절대적인 신뢰감이 없고, 위의 수식도 먹지 않는다..... 
뭐.. 안 믿으시면 한 번 해보시라.. --;;

그럼.. 어떻게 해야할까?
우리의 친구 꼼수를 한 번 발동 시켜볼까?

* 해법 *
************************************************************************************************
# [Oracle]
1. 아래 쿼리는, 데이터가 실제적으로 들어간 순서에 맞게 rownum을 부여 하여,
그 값을 하나의 컬럼으로 출력될 수 있게 해준다.
SELECT a.rownum as rnum, a.love_id
FROM love a;

2. 1에서 만들어진 쿼리를 아래와 같이 서브쿼리화 하여, between 사용이 가능해진다.
SELECT b.love_id
FROM (SELECT a.rownum as rnum, a.love_id FROM love a) as b
WHERE b.rnum BETWEEN 1000 AND 2000;

************************************************************************************************

자, 그럼,, 저 위의 방식을 이용하여 마음껏(???) limit 인 척 해보자.


출처 : http://blackbull.tistory.com/25

Posted by 1010
02.Oracle/DataBase2014. 7. 23. 15:06
반응형

DBMS별 지원되는 최대 컬럼수(Oracle,ms-sql,mysql,db2)


지원되는 최대 컬럼수

사용된 DBMS
Oracle: Oracle8i Enterprise Edition Release 8.1.7.0.1 - Production
DB2   : DB2/NT 7.2.1
MySQL : 4.0.7-gamma-nt
MS-SQL: Microsoft SQL Server  2000 - 8.00.760 (Intel X86) 
            Enterprise Edition on Windows NT 5.0 (Build 2195: Service Pack 3)

사용된 질의
create table long_test
(
id1     varchar(10),
id2     varchar(10),
id3     varchar(10),
id4     varchar(10),
id5     varchar(10),
id6     varchar(10),
id7     varchar(10),
id8     varchar(10),
id9     varchar(10),
id10    varchar(10),
id11    varchar(10),
id12    varchar(10),
.
.
.



오라클과 ms-sql의 경우, 최대 1000, 1024개의 컬럼을 지원한다고 명시되어 있다.
MySQL의 경우, 명시적인 언급을 찾을 수 없어서 실험을 통해 얻은 값이다.
DB2의 경우는 table space의 페이지크기와 컬럼의 데이터타입에 따라 가변적이다.

오라클 : 1000
MySQL  : 3230
MS-SQL : 1024
DB2    : 가변적. 최대 컬럼수를 구하는 공식은 다음과 같다.
         4K page size allows a maximum of 500 columns 
         8K, 16K, and 32K page sizes allow a maximum of 1012 columns. 
         The actual number of columns for a table is determined by the following formula: 
         Total Columns * 8 + Number of LOB Columns * 12 + Number of Datalink Columns * 28 <= row size limits for page size. 

This article comes from dbakorea.pe.kr (Leave this line as is) 

출처 : https://www.linux.co.kr/home2/board/subbs/board.php?bo_table=lecture&wr_id=233&sca=3&page=9


Posted by 1010
반응형

if (ex instanceof DataAccessException) {

       SQLException se = (SQLException) ((DataAccessException) ex).getRootCause();

       msg = se.getMessage();

       LOGGER.debug("****** DataAccessException : {} // {}", se.getErrorCode(), se.getMessage());

}


if (ex instanceof BadSqlGrammarException) {

       SQLException se = ((BadSqlGrammarException) ex).getSQLException();

       LOGGER.debug("**BadSqlGrammarException {} ", se.getErrorCode());

}

else if (ex instanceof InvalidResultSetAccessException) {

       SQLException se = ((InvalidResultSetAccessException) ex).getSQLException();

       LOGGER.debug("**InvalidResultSetAccessException {} ", se.getErrorCode());

}

else if (ex instanceof DuplicateKeyException) {

       LOGGER.debug("**DuplicateKeyException {} ", ex.getMessage());

}

else if (ex instanceof DataIntegrityViolationException) {

       LOGGER.debug("**DataIntegrityViolationException {} ", ex.getMessage());

}

else if (ex instanceof DataAccessResourceFailureException) {

       LOGGER.debug("**DataAccessResourceFailureException {} ", ex.getMessage());

}

else if (ex instanceof CannotAcquireLockException) {

       LOGGER.debug("**CannotAcquireLockException {} ", ex.getMessage());

}

else if (ex instanceof DeadlockLoserDataAccessException) {

       LOGGER.debug("**DeadlockLoserDataAccessException {} ", ex.getMessage());

}

else if (ex instanceof CannotSerializeTransactionException) {

       LOGGER.debug("**CannotSerializeTransactionException {} ", ex.getMessage());

}

org.springframework.jdbc.support.SQLErrorcodesSQLExceptionTranslator Class


Posted by 1010
반응형

Index of /asdoc/mx/charts

 Name                    Last modified      Size  Description
Parent Directory - AreaChart.html 2014-05-03 13:10 304K AxisLabel.html 2014-05-03 13:10 13K AxisRenderer.html 2014-05-03 13:10 296K BarChart.html 2014-05-03 13:10 307K BubbleChart.html 2014-05-03 13:10 305K CandlestickChart.html 2014-05-03 13:10 304K CategoryAxis.html 2014-05-03 13:10 60K ChartItem.html 2014-05-03 13:10 28K ColumnChart.html 2014-05-03 13:10 313K DateTimeAxis.html 2014-05-03 13:10 101K GridLines.html 2014-05-03 13:10 252K HLOCChart.html 2014-05-03 13:10 303K HitData.html 2014-05-03 13:10 24K Legend.html 2014-05-03 13:21 418K LegendItem.html 2014-05-03 13:21 251K LineChart.html 2014-05-03 13:21 302K LinearAxis.html 2014-05-03 13:21 54K LogAxis.html 2014-05-03 13:21 51K PieChart.html 2014-05-03 13:21 294K PlotChart.html 2014-05-03 13:21 301K chartClasses/ 2014-05-03 13:35 - class-list.html 2014-05-03 13:10 2.0K effects/ 2014-05-03 13:35 - events/ 2014-05-03 13:35 - package-detail.html 2014-05-03 13:21 8.7K renderers/ 2014-05-03 13:35 - series/ 2014-05-03 13:35 - styles/ 2014-05-03 13:35 -
Apache/2.4.9 (Unix) OpenSSL/1.0.1h Server at flex.apache.org Port 80


Posted by 1010
카테고리 없음2014. 6. 24. 17:24
반응형

FlexLib component list

This wiki page contains a list of all components contained in the current build of flexlib. After adding new components to FlexLib, you should edit this page and include a short summary of the component and the links to the ASDoc documentation and the sample SWF file, if available. Include the names of contributors for each component.

package: flexlib.controls

Base64Image

An image component that renders base-64 encoded images.

Documentation | Example | Contributor: Stacy Young

CanvasButton

The CanvasButton is an extension of Button that lets you set the contents of the Button to any UI components, as opposed to only a single icon and label like the normal Button control allows.

Documentation | Example | Contributor: Doug McCune

CanvasButtonAccordionHeader

The CanvasButtonAccordionHeader allows you to use a CanvasButton component as the header renderer of an Accordion, meaning you have full control over the header renderer and can add additional UI control, etc.

Documentation | Example | Contributor: Doug McCune

ConvertibleTreeList

A control that combines the functionality of the Tree component and the List component. The ConvertibleTreeList allows you to use one control and have it display the dataProvider either as a Tree or as a List. You only have to set the dataProvider once. You can set the mode at any time to change the display mode.

Documentation | Example | Contributor: Doug McCune

Fire

A component to produce a simulated fire effect.

Documentation | Example | Contributor: Doug McCune

Highlighter

A component to highlight selected words or phrases within a text control

Documentation | Example | Contributor: Tom Lee

HSlider and VSlider

Alternatives to the HSlider and VSlider controls included in the Flex framework. This version of the HSlider and VSlider allows you to drag the region between the thumbs, if the slider has mutliple thumbs. If there is more than one thumb then the region between the leftmost thumb and the rightmost thumb is draggable.

Documentation | Example | Contributor: Doug McCune

IconLoader

The IconLoader component converts a Macintosh OS X (.icns) or Windows XP (.ico) icon file, along with its alpha mask, to a Flex 2 UIComponent.

Documentation | Example | Contributor: Tobias Patton

ImageMap

The ImageMap control is an implementation of a client-side image map component, like it is supported in HTML.

Documentation | Example | Contributor: Doug McCune

PromptingTextInput

The PromptingTextInput component is a small enhancement to standard TextInput. It adds the ability to specify a prompt value that displays when the text is empty, similar to how the prompt property of the ComboBox behaves when there is no selected value.

Documentation | Example | Contributor: Darron Schall

PromptingTextArea

The PromptingTextArea component is a small enhancement to standard TextArea. It adds the ability to specify a prompt value that displays when the text is empty, similar to how the prompt property of the ComboBox behaves when there is no selected value.

Documentation | Example | Contributor: Darron Schall

ScrollableArrowMenu

An extension of ScrollableMenu that uses two arrow buttons placed at the top and bottom of the menu for scrolling.

Documentation | Example | Contributor: Doug McCune

ScrollableMenu

An extension of mx.controls.Menu that allows the control to scroll vertically.

Documentation | Example | Contributor: Doug McCune

ScrollableMenuBar

ScrollableMenuBar is an extension of MenuBar that uses flexlib.controls.ScrollableMenu instead of using the original mx.controls.Menu. This allows us to specify a maxHeight for the ScrollableMenuBar and that maxHeight will be used to determine the maxHeight for all the menus that the component generates.

Documentation | Example | Contributor: Doug McCune

ScrollablePopUpMenuButton

ScrollablePopUpMenuButton is an extension of PopUpMenuButton that uses flexlib.controls.ScrollableMenu instead of using the original mx.controls.Menu, which adds scrolling functionality to the menu.

Documentation | Example | Contributor: Doug McCune

SuperTabBar

The SuperTabBar control extends the TabBar control and adds drag and drop functionality and closable tabs. Used by the SuperTabNavigator.

Documentation | Example | Contributor: Doug McCune

TreeGrid

A control that combines the functionality of the Tree component and the DataGrid component.

Documentation | Example | Contributor: Yaniv De Ridder

VerticalMenuBar

A VerticalMenuBar control defines a vertical, top-level menu bar that contains one or menu items, just like the horizontal MenuBar control.

Documentation | Example Contributor: Doug McCune

package: flexlib.containers

AdvancedForm

The Advanced Form component provides Reset, Undo and Redo functionality. Undo and Redo are accessed by pressing "ctrl-Z" and "ctrl-Y" respectively.

Documentation | Example | Contributor: Renaun Erickson

ButtonScrollingCanvas

This is a Container component that uses buttons placed over the edges of the container to do the scrolling of its children. These buttons can either scroll the children when the user moves the mouse over them, or via mouse click. The buttons can be fully skinned.

Documentation | Example | Contributor: Doug McCune

Docking ToolBar

The docking toolbar component collectively helps managing toolbars in an application. The toolbars can be docked to the top or bottom edges or kept floating within application area. The contents within a toolbar use a flow based layout approach.

Documentation | Example | Contributor: Adobe Flex Team (Bangalore)

DragScrollingCanvas

This is a Container component that allows the user to drag the contents to scroll the children, instead of or in addition to using the scrollbars.

Documentation | Example | Contributor: Doug McCune

HAccordion

A Horizontal Accordion component. Works exactly like the normal Accordion component, except the item headers are rotated and they cascade from left to right, instead of top to bottom.

Documentation | Example | Contributor: Doug McCune

FlowBox

The FlowBox is an extension of Box that implements a FlowLayout algorithm for layout out children. FlowBox will position children in a horizontal fashion. When the width of the child exceeds the width of the container, the child is placed on a new row.

Documentation | Example | Contributor: Darron Schall

SuperTabNavigator

The SuperTabNavigator is an extension of the TabNavigator navigation container. It functions exactly like the TabNavigator, but adds some functionality. Added functionality includes: 1. Draggable, re-orderable tabs, 2. Closable tabs, 3. Scrolling tab bar if too many tabs are open, 4. Drop-down list of tabs

Documentation | Example | Contributor: Doug McCune

WindowShade

The WindowShade is a container that displays a header button, which when clicked, will cause a panel to "unroll" beneath it like a windowshade being pulled down; or if the panel is already displayed it will be "rolled up" like a windowshade being rolled up.

Documentation | Example | Contributor: Dave Glasser, Doug McCune

package: flexlib.charts

DraggablePie

The DraggablePie is a chart a pie chart where each wedge is draggable the user. It is used so that users can manipulate the data displayed in a pie chart. The pie chart can be configured to dispatch changes while dragging, or when the dragging is complete.

Documentation | Example | Contributor: Billy Schoenberg

HorizontalAxisDataSelector

The HorizontalAxisDataSelector is a chart annotation that allows the user to select a value along the x axis of the chart. The selector dispatches a change event with the values of each of the y values of the chart series at the given x axis location.

Documentation | Example | Contributor: Darron Schall

package: flexlib.skins

EnhancedButtonSkin

This is the enhanced version of the Halo ButtonSkin that has been customized to support four numeric values in the corner, a more configurable border, and also a large number of colors in the gradient, individually specified for up, over, etc states.

Documentation | Example | Contributor: Daniel Wabyick

package: flexlib.scheduling

Put some better Scheduling Framework documentation here, or put a link to a separate wiki page?

Original Adobe Labs wiki entry

Documentation | Example 1 demo source | Example 2 demo source | Example 3 demo source | Example 4 demo source | Example 6 demosource | Example 7 demo source | Contributor: Adobe Consulting, Wim Roelens Contributor: Adobe Consulting



출처 : https://code.google.com/p/flexlib/wiki/ComponentList

Posted by 1010
반응형

There are a lot of Flex Explorer applications available on line. These explorers can really help when you want to refresh your knowledge on the Flex capabilities or when you plan to fine tune specific styles or effects.

Whether you’re trying to design a good looking button or data grid, configuring a fancy glow effect, implementing a complex regular expression, configuring a BlazeDS channel or just searching for inspiration; there is probably a Flex Explorer for you.

Here is my own list of explorers, that I also gathered in my Flex Tool Box page for quick and easy reference.

Flex Component Explorer

Flex Component Explorer screen capture
The Flex Component Explorer lets you browse through the huge list of available Flex core components. For each of them, a small sample application (corresponding code is available) shows how the component is rendered and behaves. Even if you can’t change the component configuration, this remains a useful tool when you’d like to learn more of a component that you never used so far.

Tour de Flex Explorer

Tour De Flex screen capture
The Tour De Flex Explorer is available on line but can also be installed as an AIR application. It extends the Flex 3 Component Explorer by adding Flex 4 Spark components description, coding techniques examples, Flex Data Access information, Flex frameworks introduction and much more.

Flex Chart Sampler

Flex Chart Sampler screen capture
The Flex Chart Sampler (from Ely Greenfield blog) lets you interactively discover the Flex Charting components.

Flex Style Explorer

Flex Style Explorer screen capture
The Flex Style Explorer is probably the one I use the most. You can use it to style interactively any component, see how looks the result and export the result as a CSS file that you can use in your projects.

Flex Enhanced Button Skin Explorer

Flex Enhanced ButtonSkin Explorer screen capture
The Flex Enhanced Button Skin Explorer(from Daniel Wabyick blog) is an explorer dedicated to the EnhancedButtonSkin available in FlexLib.

Adobe® Kuler™ Color Themes Explorer

Kuler Color Theme Explorer screen capture
The Adobe® Kuler™ Color Themes Explorerallows experimenting quickly with color variations and browse color themes from the Kuler community (can be downloaded as an AIR application).

Flex Filter Explorer

Flex Filter Explorer screen capture
The Flex Filter Explorer (from Joe Johnston blog) lets you define your flex filter interactively and generates the corresponding MXML to include in your projects.

Flex Easing Function Explorer

Flex Easing Function Explorer screen capture
The Flex Easing Function Explorer (fromJames Ward blog) allows experimenting with ease functions that allow applying non-linear motion to effects.

Flex Distortion Effect Explorer

Flex Distortion Effect Explorer screen capture
The Flex Distortion Effect Explorer (from Alex Uhlmann blog) demonstrates fancy distortion effects such as Flip, Pop, Cube rotate, Door and much more.

Flex Reflection Explorer

Flex Reflection Explorer screen capture
The Flex Reflection Explorer (from Wietse Veenstra blog) demonstrates beautiful reflection effects using the ReflectionManager provided class.

Efflex Explorer

Efflex Explorer screen capture
The Efflex Explorer (from Efflex.org) demonstrates the effects available when using the open source Efflex library.

Flex Regular Expression Explorer

Flex Regular Expression Explorer screen capture
The Flex Regular Expression Explorer (fromRyan Swanson blog) lets you define your regular expressions from the explorer and check immediately whether they work or not by providing test data and look at the result. This explorer also provides a very good on line help that should allow you not be afraid by regular expressions anymore.

BlazeDS Channel Designer

BlazeDS Channel Designer screen capture
The BlazeDSChannel Designer isn’t really an explorer but I thought it would be useful to mention it here. It provides an easy and interactive way to configure your BlazeDS channels and outputs the services-config.xml corresponding to your options.

Rating: 5.0/5 (8 votes cast)


Posted by 1010
반응형

출처 : http://hapina.tistory.com/10


플렉스에서 하나의 어플리케이션에 모듈을 여러개 로딩하여 사이즈를 줄이는 방법으로 프로젝트가 많이 진행됩니다.

그러나 가끔 마우스 다운 이벤트 및 드래깅 시 다음과 같은 에러가 발생합니다.

TypeError: Error #1034: 유형 강제 변환에 실패했습니다. mx.managers::DragManagerImpl@a23f6f1을(를) mx.managers.IDragManager(으)로 변환할 수 없습니다.

또는 다음과 같은 에러...

ReferenceError: Error #1065: 변수 mx.managers::DragManager이(가) 정의되어 있지 않습니다.

데이터그리드를 넣은 모듈을 로드하여 폴더 열기/닫기 할 때 처음 이 에러를 만났습니다.

이 문제의 원인과 해결책에 대해 알아봅시다.

원인 및 해결책.

플렉스 프레임웤 상 DragManager 는 싱글톤으로 하나의 인스턴스만 존재해야 합니다.

App 에는 Manager DB 가 존재하여 모든 Manager 를 관리합니다.(모든 메니저의 인스턴스가 아닌 클래스)

처음 모듈을 로딩할 때 DragManager 가 해당 모듈에서 인스턴화되면..

두번째 모듈에서는 해당 인스턴스를 알아낼 방법이 없어집니다. 즉 혼란이 생긴거죠.

모듈의 Applicatoin shared code 때문에 생기는 일입니다.

따라서 처음 드래그메니저의 인스턴스를 App 에서 생성되도록하여 모든 모듈이 드래그메니저의 인스턴스를 App 에서 참조하도록 설정합니다.

해결책으로 이를 방지하기 위해 어플리케이션에서 다음과 같이 DragManager 를 import 하세요.

이는 아마도 버그일 수 있습니다. 최초 App 에서 드래그메니저의 인스턴스를 생성하면 되는데 그렇지 않고 있기 때문이죠.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
 <mx:Script>
  <![CDATA[

   import mx.managers.DragManager;
   private var dummy:DragManager;
   
....
...
...

  ]]>
 </mx:Script>

이 처럼 App 에서 DragManager 의 인스턴스를 생성하게 하여 클래스를 내포시킵니다.

그럼 굿럭!~

Posted by 1010
반응형

출처 : http://blog.jidolstar.com/666


Flash 애플리케이션을 제작할때 항상 고민되는 것은 메모리와 속도일 것이다. 이 문서는 이러한 고민을 했던 분들을 위해 본인의 경험을 반영하여 작성한 것이다. 주의할 것은 이 문서에서 다루는 Object Pool은 메모리, 속도 개선을 위한 하나의 방법일 뿐이지 전부는 아니다라는 것이다. Object Pool이 어떻게 당신의 애플리케이션의 속도와 메모리를 개선시켜줄 것인가 전달하는 것이 문서의 목표이다.

목차 

  1. 객체 관리에 대한 질문!
  2. new 연산자는 비싸다!
  3. Object Pool이란?
  4. Object Pool을 도입할 때 고려사항
  5. Object Pool을 사용해보자.
  6. Object Pool과 new 연산자 사용 비교
  7. 메모리 사용의 폭이 급격한 애플리케이션은 좋지 못하다.
  8. 재사용할 수 있는 클래스 제작
  9. 정리하며
  10. 참고글



1. 객체 관리에 대한 질문!
제작된 애플리케이션에 필요한 최소한의 메모리에 대해서 생각해 본적이 있는가? 하나의 애플리케이션에 들어가는 최소 자원(resource)은 얼마나 될까? 그 자원은 얼마나 자주 사용하고 버리는 것일까? 

슈팅 게임을 보면 동일한 적군전투기가 많이 나온다. 그 중에 적군전투기1이 있다고 하자. 적군전투기1이 한 화면에 5개가 나왔다. 아군전투기에 의해 죽든 화면에서 사라지든 어쨌든지 언젠간 화면에서 사라지고 쓸모없게 된다. 좀 있다가 또 적군전투기1이 3개가 나온다. 또 반복해서 사라진다. 당신이 이 슈팅 게임을 만든다면 적군전투기1 관리를 어떻게 할 것인가? 즉, 화면에 나타날때 new로 생성하고 사라지면 delete로 메모리에게 해지요청할 것인가? 아니면 어느 특정 영역에 화면에 나타날 전투기1을 미리 5개에서 10개 생성해놓고 필요할 때마다 재사용할 것인가? 

2. new 연산자는 비싸다!
new는 클래스를 통해 객체를 생성하는 연산자이다. 이는 비교적 비용이 비싼편이다. 비용이 비싸다는 말을 구체적으로 설명하자면 new를 통해 객체를 생성하여 메모리에 올리고 다 사용했다고 판단될때 delete이나 null등을 이용해 객체참조를 제거하여 가비지 컬렉터에 맡기게 되며 결과적으로 가비지 컬렉터는 이 객체를 어느 특정시점에 제거하는 일련의 객체 생성과 삭제까지 동작이 꽤 복잡하여 메모리, CPU연산등의 자원소비가 크다라는 것을 의미한다. 특히나 클래스의 크기가 크고 복잡하다면 그 비용은 더욱 갑절로 들게 된다. new 연산 사용은 이처럼 비싼 비용을 지불해야 하기 때문에 할 수 있다면 최소한으로 사용하도록 하는 것이 속도와 메모리 관리에 도움을 준다.

앞서 설명한 슈팅 게임을 보자면 적군전투기1은 수시로 보였다가 안보였다가 한다. 이 경우 new, delete를 반복한다면 경우에 따라서 엄청난(?) 비싼 비용을 지불하게 된다. 

다른 예를 들어보겠다. 본인은 얼마전에 스타플(http://starpl.com)이라는 사이트에서 제공하는 타임라인 애플리케이션을 Flash로 개발했다. 타임라인은 사용자의 기록, 앨범등을 시간순으로 보여주게 되며 좌우측으로 이동해서 내 인생의 기록을 보는듯한 느낌을 주도록 만들었다. 이때 화면에 보여지는 사용자의 기록들이 수시로 보여졌다가 사라진다. 이때 사용된 기록을 보여주는 시각객체(DisplayObject)를 new와 delete를 반복해서 생성 및 삭제한다면 너무 비싼 비용을 들이는 것과 같다. 

스타플의 타임라인

참고글 : 스타플 타임라인 업그레이드 및 앨범 기능 추가 소식

앞의 2가지 예에서 언급했듯이 자주 보여지고 사라지는 객체를 필요할 때마다 new연산자를 사용하는 것보다는 어느 특정 영역에 미리 생성해놓고 재사용을 반복하는 구조를 도입한다면 메모리와 CPU연산에 있어서 큰 비용절감을 기대할 수 있게 된다. 

3. Object Pool이란?
Object Pool(객체 풀)은 객체를 미리 만들어놓고 담아놓는 메모리 영역이다. 개발자는 이 Object Pool에 미리 만들어진 객체를 사용하고 다 사용한 뒤에는 다시 반환시킴으로서 재사용(또는 대여)을 하도록 한다. Object Pool을 사용하면 처음 객체를 생성할때와 더 필요한 객체가 있는 경우를 제외하고 new 연산을 최소화 할 수 있다. 

4. Object Pool을 도입할 때 고려사항 
Object Pool은 미리 객체를 생성하고 지속적으로 재사용하기 위해 사용한다. 이때 미리 객체를 생성한다는 것은 초반 애플리케이션 동작 퍼포먼스에 영향을 미칠 수 있다. 

Object Pool을 사용했을때 다음과 같은 장점을 얻을 수 있다.

1. new 연산자를 자주 사용해야하는 경우에 도입하면 애플리케이션 속도 향상을 기대할 수 있다. 
2. 가비지 컬렉터에 의존하지 않는 메모리 관리를 꾀할 수 있다.

하지만 일반적으로 10개가 필요한데 미리 100개를 만들어 항상 10% 미만 정도의 재사용률을 보인다면 그것은 메모리 낭비이다. 그러므로 다음과 같은 조건에서 Object Pool을 사용하고 관리하는 것이 좋겠다.

1. 비교적 큰 용량의 객체를 자주 사용되거나 반환해야하는 경우에 사용한다.
2. 미리 생성할 객체를 적당하게 정하도록 한다.

반대로 위 경우가 아니라면 쓸데없이 Object Pool을 사용하지 말자. 


5. Object Pool을 사용해보자.

폴리고널 lab에서 간단한 Object Pool을 공개했다. 아래 링크에서 다운로드 받을 수 있다.


사용 방법은 매우 간단하다. ObjectPool 클래스는 아래 코드처럼 특정 클래스를 할당(allocate)하면 된다.

var isDynamic:Boolean = true;

var size:int = 100;

var pool:ObjectPool = new ObjectPool(isDynamic);

pool.allocate(MyClass, size);


isDynamic 플래그는 Object Pool내의 객체가 비어있는가 체크하기 위해 사용한다. 즉, true이면 Pool은 자동적으로 정해진 크기(size)를 넘어서더라도 Pool의 크기를 확장하여 새로운 객체를 줄 수 있으나 false이면 정해진 크기만큼 객체를 이미 준 경우에는 Pool이 비게 되므로 Error를 발생시킨다. 

크기(size)를 100으로 지정했기 때문에 미리 Object Pool에 100개의 MyClass의 객체를 생성해 두도록 한다. 

이제부터 MyClass는 아래와 같은 방식으로 사용하면 되겠다.

myObjectArray[i] = new MyClass();

// 코드 대신 아래를 사용한다.

myObjectArray[i] = pool.instance;


다 사용한 MyClass객체는 Object Pool에 반환해줘야 한다. 이때 가비지 컬렉터의 기능을 이용하는게 아니라 재사용(recycle)을 위해서 다음과 같이 코딩하면 되겠다.

pool.instance = myObjectArray[i];


사용방법은 이게 전부다. 

원한다면 자신의 애플리케이션에 맞게 이 공개된 ObjectPool을 개선시킬 수 있다. 가령, 100개의 Pool을 만들어놓지만 처음부터 100개를 생성해놓지는 않겠고 필요할 때마다 생성해주는 구조인 게으른(lazy) 생성을 기대할 수 있을 것이다. 또는 DisplayObject의 경우라면 사용한다는 것을 DisplayObject.parent 속성으로 판단할 수 있다. 위 처럼 pool.instance = '다 사용한 객체'와 같은 형태로 반환하는 것보다 자신의 부모가 존재하지 않는다면 재사용할 객체로 판단하도록 만들면 일종의 자동 Object Pool을 만들 수 있다. 자동 Object Pool은 다음 링크를 참고하면 좋겠다. 

자동 풀링(auto pooling)을 구현해보자.



6. Object Pool과 new 연산자 사용 비교 

Object Pool을 사용해야하는 타당성은 퍼포먼스 실험을 통해 쉽게 알 수 있다.  이러한 점에서 폴리고널 랩에서 제공하는 실험은 매우 유용한 자료이다. 

실험 방법은 아래와 같다. (폴리고널 랩에서는 이 실험을 Window Vista, Flash Player 9.0.124에서 진행했다.) 그리고 Pool크기는 100으로 지정했다. 

첫번째로 아래 코드처럼 k수 만큼 Object Pool로 부터 객체를 get만 한다.

for (var i:int = 0; i < k; i++) instances[i] = p.instance;


두번째로 아래 코드처럼 k수 만큼 Object Pool로부터 객체를 get/set 한다.

for (i = 0; i < k; i++) instances[i] = p.instance;

for (i = 0; i < k; i++) p.instance = instances[i];


세번째로 아래 코드처럼 k수 만큼 Object Pool대신 new 연산자를 사용한다.

for (i = 0; i < k; i++) instances[i] = new MyClass();



결과는 다음과 같다.


네이티브 Object 클래스의 경우 Object Pool을 사용하는 것이 new 연산자를 사용한 것보다 5배 정도 빨랐다.


Object 보다 더 크고 복잡한 flash.geom.Point의 경우는 약간더 빠른 결과를 보인다.


 flash.display.Sprite 객체를 가지고 하면 무려 80배나 속도 개선이 되었다. 이는 복잡하고 큰 클래스에 Object Pool을 도입하면 효과적일 수 있다고 판단할 수 있다.

이 실험을 통해 적절한 방법으로 Object Pool을 사용하는 것은 애플리케이션의 퍼포먼스 증가에 지대한 영향을 줄 수 있다는 것을 깨달을 수 있다.


7. 메모리 사용의 폭이 급격한 애플리케이션은 좋지 못하다.
본인은 어떤 애플리케이션(Flash가 아니더라도)이든지 그 애플리케이션에 필요한 최소한의 메모리는 항상 존재한다고 생각한다. 무조건 메모리 사용을 줄여보겠다고 new, delete를 반복하는 것은 잘못된 것이다. new, delete를 반복하면 오히려 CPU 및 메모리 사용의 반복을 부축이며 애플리케이션의 전체적인 퍼포먼스를 저하시키는 경우가 발생할 수 있다. 실제로 new,delete를 반복하는 구조로 개발한 애플리케이션을 Flash Builder의 프로파일링 기능을 통해 메모리 사용을 살펴보면 큰폭으로 메모리 사용/해지가 일어난다는 것을 확인할 수 있다. 그러므로 몇몇 사람들이 Object Pool과 같은 개념과 같이 퍼포먼스 향상에 도움이 되는 개념을 전혀 사용하지 않고 Flash는 느리고 가비지 컬렉터 동작은 비정상적이다라고 말하는 것은 섣부른 판단이다라고 생각한다. 얼마든지 메모리를 효율적으로 사용하고 속도 개선을 할 수 있음에도 불구하고 잘못된 개발 방식때문에 전체적인 퍼포먼스가 나빠지는 것은 결국 개발자의 잘못인 것이다. 이는 Flash든 어떤 언어든 동일한 생각으로 접근해야 한다.

Flash Builder의 프로파일링 기능을 이용해 Object Pool과 new 연산자간의 메모리 변화를 확인할 수 있는 간단한 실험을 해보겠다. (프로파일링 기능을 사용하는 방법은 주제에서 벗어나므로 제외하겠다.)

먼저 Shape객체를 확장해서 크기와 색이 렌덤하게 그려지는 클래스를 만든다.

//화면에 출력할 Shape

class RandomShape extends Shape {

        static private var colorPool:Array = [0xff0000,0x00ffff,0x0000ff,0x00ff00,0x0f0f0f,0xf0f0f0,0xffff00,0xf00cf0,0x00fcaa,0xff0c9a];

        public function RandomShape():void {

        }

        public function draw():void {

               var radius:Number = Math.random() * 40;

               var color:uint = colorPool[ Math.floor(Math.random()*9) ];

               graphics.clear();

               graphics.beginFill(color,1.0);

               graphics.drawCircle( 0, 0, radius );

               graphics.endFill();

        }

}


위 클래스를 가지고 자동 Pool을 구현한 클래스를 만든다.(참고 : http://www.diebuster.com/?p=1000)

//Shape 자동 객체 Pool

class RandomShapeAutoPool {

        private var _pool:Vector.<RandomShape>;

        public function RandomShapeAutoPool() {

               //적당히 수로 초기화한다.

               _pool=new Vector.<RandomShape>();

        }

 

        public function getShape():RandomShape {

               var key:*, result:RandomShape;

               //먼저 기존의 pool에서 찾아본다.

               for (key in _pool) {

                       //만약 해당 객체가 null이라면 new 통해 생성한다.

                       if (_pool[key] === null) {

                              _pool[key]=new RandomShape;

                              result=_pool[key];

                              break;

                       //null 아니라면 parent 조사하여 사용가능한지 판단한다.

                       else if (_pool[key].parent === null) {

                              result=_pool[key];

                              break;

                       }

               }

               //기존의 pool안에서 쓸만한  찾지 못했다면

               if (result === null) {

                       result=new RandomShape;

                       _pool[_pool.length]=result;

               }

               //인스턴스를 반환한다.

               return result;

        }

}


이제 테스트할 수 있는 호스트코드를 제작해보겠다.

package {

        import flash.display.*;

        import flash.events.*;

        import flash.text.*;

        import flash.utils.*;

 

        [SWF(backgroundColor="#ffffff", frameRate="60", width="400", height="400")]

        public class ObjectPoolTest extends Sprite {

               private var pool:RandomShapeAutoPool=new RandomShapeAutoPool;

               private var poolFlag:Boolean = false;

               private var textPoolFlag:TextField;

               private var shapeCanvas:Sprite;

 

               public function ObjectPoolTest() {

                       addEventListener(Event.ADDED_TO_STAGE, ADDED_TO_STAGE);

               }

 

               private function ADDED_TO_STAGE($e:Event):void {

                       removeEventListener(Event.ADDED_TO_STAGE, ADDED_TO_STAGE);

                       stage.scaleMode=StageScaleMode.NO_SCALE;

                       stage.align=StageAlign.TOP_LEFT;

                       //rf)http://blog.jidolstar.com/656

                       if (stage.stageWidth === 0 && stage.stageHeight === 0) {

                              stage.addEventListener(Event.ENTER_FRAME,function($e:Event):void {

                                             if (stage.stageWidth > 0 || stage.stageHeight > 0) {

                                                     stage.removeEventListener($e.type, arguments.callee);

                                                     init();

                                             }

                                      });

                       else {

                              init();

                       }

               }

 

               private function init():void {

                       //shape 부모객체

                       shapeCanvas = new Sprite;

                       addChild( shapeCanvas );

                       //PoolFlag 상태표시

                       textPoolFlag = new TextField();

                       textPoolFlag.text = "poolFlag=" + poolFlag;

                       addChild(textPoolFlag);

                       //마우스 클릭 영역

                       graphics.beginFill(0x000000,0);

                       graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight);

                       graphics.endFill();

                       addEventListener(MouseEvent.CLICK, MOUSE_CLICK);

                       //반복동작     

                       addEventListener(Event.ENTER_FRAME, ENTER_FRAME);

               }

 

               private function MOUSE_CLICK($e:MouseEvent):void {

                       //마우스 클릭때마다 poolFlag 상태를 전환

                       poolFlag = !poolFlag;

                       textPoolFlag.text = "poolFlag=" + poolFlag;

               }

              

               private function ENTER_FRAME($e:Event):void {

                       var shape:RandomShape, i:int, numChildren:int;

                       //poolFlag 따라서 new연산자 또는 Object Pool에서 객체 참조

                       shape = poolFlag ? pool.getShape() : new RandomShape;

                       shape.draw();

                       shape.x = Math.random() * stage.stageWidth;

                       shape.y = 0;

                       shapeCanvas.addChild( shape );

                       //계속 아래로 떨어뜨림화면에서 벗어나면 removeChild시킴

                       numChildren = shapeCanvas.numChildren;

                       for( i=0; i<numChildren;i++) {

                              shape = shapeCanvas.getChildAt(i) as RandomShape;

                              shape.y += 5;

                              if( shape.y > stage.stageHeight ) {

                                      shapeCanvas.removeChild( shape );

                                      i--;

                                      numChildren--;

                              }

                       }

               }

        }

}


동작은 매우 단순하다. poolFlag가 true이면 제작한 자동 Object Pool을 사용하겠다는 것이고 false이면 new 연산자를 사용해 RandomShape객체를 생성하겠다는 것이다. poolFlag 전환은 마우스 클릭으로 할 수 있다. 그리고 비가 내리는 것처럼 Shape 객체는 위에서 부터 아래로 떨어지며 맨 아래에 닿으면 removeChild 시킨다.

테스트 화면


실행 코드와 결과물은 http://wonderfl.net/code/805dc65d1f7800b7bee5dc3cd72ca21c0a87c2d6 에서도 볼 수 있다.

이제 Flash Builder의 프로파일링 기능을 이용해 메모리 변화 및 객체 생성빈도를 살펴보자.

먼저 new 연산자를 사용했을때 메모리 변화를 보자. 

new 연산자로 객체생성시 Memory Usage


new 연산자를 사용해 객체를 생성하고 필요없을때 removeChild를 하게 되면 가비지 컬렉션 처리가 되기 때문에 일정한 시점에 메모리가 해지되는 것을 반복하는 것을 확인할 수 있다.

new 연산자로 객체생성시 Live Object


위 표는 축적된 객체수(Cumulative Instances), 현재 객체수(Instances), 축적된 메모리(Cumulative Memory), 현재 메모리(Memory)를 나타낸다. 변화 무쌍하며 실제로 보여지지 않는 객체도 가비지 컬렉션 처리가 일어날때까지 메모리에 남아 있다. 분명 비효율적으로 보인다.

반대로 Object Pool을 이용했을때 메모리 변화이다.

Object Pool로 객체관리시 Memory Usage

new 연산자를 사용했을때와 비교가 안될 정도로 고요한 느낌이 든다. 거의 일직선의 메모리 사용율을 보인다.

Object Pool로 객체관리시 Live Object


축적되는 객체도 81개를 넘지 않는다. 그만큼 객체 생성과 삭제에 민감하지 않게 되고 가비지 컬렉터 도움을 받지 않고 객체관리가 되는 것을 확인할 수 있다.


8. 재사용할 수 있는 클래스 제작

Object Pool을 사용시 한가지 고려할 사항은 재사용할 수 있는 클래스를 만들어야 한다는 점이다. 왜냐하면 Object Pool은 단순히 재사용을 위한 공간만 제공하는 것이지 재사용을 하기 위한 어떤 기능을 클래스에게 줄 수 없기 때문이다. 그래서 재사용이 가능한 클래스를 설계하는 것은 하나의 기술적 이슈가 될 수 있다.  

재사용할 수 있는 클래스를 어떻게 만드는지 간단한 예를 들어보겠다.

final public class MyData {

        public var type:String;

        public var id:int;

        public var name:String;

}


위 클래스는 데이터를 담는 클래스이다. id, name은 실제 사용하는 데이타 값들이고 이 데이타의 종류는 type으로 구분한다. 이 데이타를 사용하는 인터페이스를 제작해보겠다.

public interface IMyClass {

        function init($data:MyData):void;

        function clear():void;

        function get data():MyData;

}


이 인터페이스는 앞으로 만들 MyClass01, MyClass02... 등을 구현하기 위한 것이다. 초기화 하기 위해 init()함수가 있고 인자로 위에서 제작한 MyData 객체를 받는다. 또한 사용할 필요가 없을때 내부 청소를 위해 clear()함수를 추가했다. 게다가 가지고 있는 data를 참고하기 위해 get data()도 만들었다. 이제 이 인터페이스를 구현한 클래스는 아래처럼 만든다.

final internal class MyClass01 extends Sprite implements IMyClass {

        private var _data:MyData;

        public function MyClass01() {

        }

        public function init($data:Mydata):void {

               _data = $data;

               //구현

        }

        public function clear():void {

               _data = null;

        }

        public function get data():MyData {

               return _data;

        }

}

 

final internal class MyClass02 extends Sprite implements IMyClass {

        private var _data:MyData;

        public function MyClass02() {

        }

        public function init($data:Mydata):void {

               _data = $data;

               //구현

        }

        public function clear():void {

               _data = null;

        }

        public function get data():MyData {

               return _data;

        }

}

 

final internal class MyClass03 extends Sprite implements IMyClass {

        private var _data:MyData;

        public function MyClass03() {

        }

        public function init($data:Mydata):void {

               _data = $data;

               //구현

        }

        public function clear():void {

               _data = null;

        }

        public function get data():MyData {

               return _data;

        }

}



코드가 길어보이지만 3개 클래스 모두 IMyClass를 구현했고 Sprite를 확장했다. 단지 클래스 이름만 다르며 실제 구현부는 알아서 구현하면 된다. 

개발자는 MyClass01, MyClass02, MyClass03은 매우 자주 new 연산자로부터 생성되는 클래스로 판단했고 그래서 Object Pool을 도입하겠다고 결정했다고 하자. 그럼 다음과 같이 시도해 볼 수 있다.

import de.polygonal.core.ObjectPool;

import flash.display.DisplayObject;

 

public class MyPool {

        private static var poolList:Object;

       

        static public function init():void {

               poolList = {

                       'type01':new ObjectPool(true),

                       'type02':new ObjectPool(true),

                       'type03':new ObjectPool(true),

               };

               poolList.type01.allocate(20, MyClass01);

               poolList.type02.allocate(10, MyClass02);

               poolList.type03.allocate(100, MyClass03);

        }

       

        static public function getObject($data:MyData):IMyClass {

               if( $data === null ) {

                       throw new Error('인자값은 null이면 안됩니다.');

               }

               try {

                       var object:IMyClass = poolList[$data.type].object;

                       object.init($data);

               catch {

                       throw new Error('데이타의 type값이 잘못된 값입니다.');

               }

               return object;

        }

       

        static public function returnObject($object:IMyClass):void {

               if( $object === null ) {

                       throw new Error('인자값은 null이면 안됩니다.');

               }

               $object.clear();

               if( DisplayObject($object).parent ) {

                       DisplayObject($object).parent.removeChild( DisplayObject($object) );

               }

               poolList[$object.data.type].object = $object;

        }

}



위에서 제작된 MyPool은 static클래스이며 ObjectPool을 이용해 IMyClass 인터페이스를 구현한 클래스를 init()함수에서 적당하게 할당하는 것을 확인할 수 있다. 또한 getObject()를 통해 인자값 data를 참고하여 참조할 Object Pool을 찾아 IMyClass를 구현한 각각의 클래스의 객체를 받아올 수 있으며 returnObject를 통해 다 사용한 객체를 반환한다. 

실제로 실무에서 이런 형태로 개발했고 이는 매우 유용했다. MyPool 클래스를 더 개선해서 더 많은 수 Object Pool을 감당할 수 있도록 확장할 수 있다면 더욱 유용해질 것이라 생각한다.


9. 정리하며 

Object Pool 개념은 Flash에만 국한되는 유용한 개념이 아니다. 이런 개념들에 대한 노하우를 계속 쌓아간다면 당신의 애플리케이션은 더욱 좋은 작품으로 탈바꿈할 수 있을 것이다. 

Flash Player가 느리고 메모리 관리가 안된다고 생각하지는 말자. Flash Player 태생자체가 그런것을 어찌하겠는가? 제작한 애플리케이션이 느리다면 그것은 결국 개발자가 잘못한 것임을 항상 인지하자. 중요한 것은 여전히 Flash Player는 유용하고 많이 사용되고 있으며 지금도 나날이 발전하여 Cross OS, Cross Browser를 넘어 Cross Device 세계로 뻗어가고 있다는 점이다.


10. 참고글

글쓴이 : 지돌스타(http://blog.jidolstar.com/666

Posted by 1010
반응형

TextInput 의 restrict property 에 대해...

<mx:TextInput id="txt" restrict="ㄱ-힣">

restrict = "ㄱ-힣"            :  한글만 입력가능.
restrict = "A-z"              :  영어만 입력가능.
restrict = "0-9"               :  숫자만 입력가능.
restrict = "0-9\-"           : 숫자와 '-'만 입력가능.
restrict = "^a-z"             : 소문자 a부터 z까지 제외한 모든문자 입력가능.
restrict = "A-z0-9\@\." : 이메일 형식만 입력가능.
restrict = "\\\"           : '\' 문자만 입력가능.
restrict = "\^"               :  '^' 문자만 입력가능.

여러가지 응용도 하실 수 있겠습니다.


출처 : http://2rang.tistory.com/15

Posted by 1010
반응형

 <mx:DataGridColumn headerText="선택" width="50">
              <mx:itemRenderer>
               <mx:Component>
                <mx:HBox horizontalAlign="center">
                 <mx:Script>
                  <![CDATA[
                               public override function set data(value:Object):void
                               {
                                   super.data = value;
                               }
                  ]]>
                 </mx:Script>
                 <mx:CheckBox id="cb1" selected="{data.chk}" change="{data.chk = cb1.selected}"/>
                </mx:HBox>
               </mx:Component>
              </mx:itemRenderer>
             </mx:DataGridColumn>


출처 : http://blog.naver.com/PostView.nhn?blogId=neodijkstra&logNo=50050005320

Posted by 1010
반응형

기본적인 개념으로.. 데이터 그리드등에 ItemRenderer를 사용하여 checkBox 나 기타 등등의 컴포넌트를 넣었을때

데이터그리드를 스크롤하게 되면 체크박스의 데이터가 뒤죽박죽이 되는 문제가 발생한다.

이는.. 보이는영역에서 사라진 컴포넌트들이 재사용(!!) 되면서 일어나는 현상으로 다음과 같은 방법으로 해결한다.

<Adobe Flex4 Help에서 발췌>

Creating a recyclable item renderer


With virtual layout disabled, the DataGroup and SkinnableDataContainer containers create one instance of the item renderer for each child. With virtual layout enabled, the container only creates enough item renderers to display its currently visible children. Virtual layout greatly reduces the overhead required to use the DataGroup and SkinnableDataContainer containers.
With virtual layout enabled, when a child is moved off the visible area of the container, its item renderer is recycled. First, the item renderer’s data property is set to null. When the item renderer is reused, its data property is set to the data item representing the new child. Therefore, if a recycled item renderer performs any actions based on the value of the data property, it must first check that the property is not null. When the item renderer is reassigned, Flex also calls the updateRenderer() method of the item renderer owner. This method must set the owner and label properties on the item renderer. Subclasses of SkinnableDataContainer, can use the updateRenderer() method to set additional properties on the item renderer. Because a container can reuse an item renderer, ensure that you fully define its state. For example, you use a CheckBox control in an item renderer to display a true (checked) or
false (unchecked) value based on the current value of the data property. A common mistake is to assume that the CheckBox control is always in its default state of unchecked and only inspect the data property for a value of true.
However, remember that the CheckBox can be recycled and had previously been checked. Therefore, inspect the data property for a value of false , and explicitly uncheck the control if it is checked, as the following example shows:

<ItemRenderer의 dataChange 이벤트를 이용하는 방법>
Prerelease - 5 October 2009
USING FLEX 4 442
Building the user interface
<?xml version="1.0" encoding="utf-8"?>
<!-- containers\spark\myComponents\MySimpleItemRendererCB.mxml -->
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/halo"xmlns:s="library://ns.adobe.com/flex/spark" dataChange="setMgr();">
<fx:Script>
<![CDATA[
private function setMgr():void {
// Check to see if the data property is null.
if (data == null) return;
// If the data property is not null,
// set the CheckBox control appropriately..
if (data.manager == "yes") {
mgr.selected = true;
}
else {
mgr.selected = false;
}
}
]]>
</fx:Script>
<s:states>
<s:State name="normal"/>
<s:State name="hovered"/>
<s:State name="selected"/>
</s:states>
<s:HGroup verticalCenter="0" left="2" right="2" top="2" bottom="2">
<s:Label text="{data.lastName}, {data.firstName}"/>
<s:Label text="{data.companyID}"/>
<s:CheckBox id="mgr"/>
</s:HGroup>
</s:ItemRenderer>


Use the dataChange event of the ItemRenderer class to detect the change to its data property. This event is dispatched whenever the data property changes. Alternatively, you can override the data property. Alternatively, you can override the ItemRenderer.data
property itself, as the following example shows:

<data 속성을 오버라이드하는 방법>
Prerelease - 5 October 2009
USING FLEX 4 443
Building the user interface
<?xml version="1.0" encoding="utf-8"?>
<!-- containers\spark\myComponents\MySimpleItemRendererCBData.mxml -->
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/halo"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
// Check to see if the data property is null.
if (value== null) return;
// If the data property is not null,
// set the CheckBox control appropriately..
if (value.manager == "yes") {
mgr.selected = true;
}
else {
mgr.selected = false;
}
}
]]>
</fx:Script>
<s:states>
<s:State name="normal"/>
<s:State name="hovered"/>
<s:State name="selected"/>
</s:states>
<s:HGroup verticalCenter="0" left="2" right="2" top="2" bottom="2">
<s:Label text="{data.lastName}, {data.firstName}"/>
<s:Label text="{data.companyID}"/>
<s:CheckBox id="mgr"/>
</s:HGroup>
</s:ItemRenderer>

상단의 두가지 방법을 이용하여 새로운 ItemRenderer를 생성한 후 다음과 같은 방법으로 사용한다.

<?xml version="1.0" encoding="utf-8"?>
<!-- containers\spark\SparkDataGroupContainerTypicalItem.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/halo"
xmlns:s="library://ns.adobe.com/flex/spark">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
[Bindable]
public var typicalObj:Object = {
firstName:"Long first name",
lastName:"Even longer last name",
companyID:"123456",
manager:"yes"
};
]]>
</fx:Script>
<s:Scroller>
<s:DataGroup itemRenderer="myComponents.MySimpleItemRendererCB"
height="100"
typicalItem="{typicalObj}" >
<s:layout>
<s:VerticalLayout useVirtualLayout="true"/>
</s:layout>
<mx:ArrayList>
<fx:Object firstName="Bill" lastName="Smith" companyID="11233" manager="yes"/>
<fx:Object firstName="Dave" lastName="Jones" companyID="13455" manager="no"/>
<fx:Object firstName="Mary" lastName="Davis" companyID="11543" manager="yes"/>
<fx:Object firstName="Debbie" lastName="Cooper" companyID="14266" manager="no"/>
<fx:Object firstName="Bob" lastName="Martins" companyID="11233" manager="yes"/>
<fx:Object firstName="Jack" lastName="Jones" companyID="13455" manager="no"/>
<fx:Object firstName="Sam" lastName="Johnson" companyID="11543" manager="yes"/>
<fx:Object firstName="Tom" lastName="Fitz" companyID="14266" manager="no"/>
<fx:Object firstName="Dave" lastName="Mead" companyID="11233" manager="yes"/>
<fx:Object firstName="Dave" lastName="Jones" companyID="13455" manager="no"/>
<fx:Object firstName="Mary" lastName="Davis" companyID="11543" manager="yes"/>
<fx:Object firstName="Debbie" lastName="Cooper" companyID="14266" manager="no"/>
</mx:ArrayList>
</s:DataGroup>
</s:Scroller>
</s:Application>


Posted by 1010
반응형

출처 : http://igna84.blogspot.kr/2010/03/flex-datagrid-itemrenderer-2-%EC%A0%84%EB%A9%B4%EC%A0%84.html


Flex DataGrid ItemRenderer #2 - 전면전

경력사원임에도 불구하고 나이가 어려서 그런지 신입사원 연수를 다녀오라는 지시에 지난 목요일 금요일 다녀오는 바람에 ItemRenderer 이야기가 늦어졌습니다. 기다리신 분 계신가요?? 죄송합니다. 본의아니게;;

아무튼 DataGrid ItemRenderer 라는 뜨거운 감자를 삼키기위해 어떤 모양의 ItemRenderer를 만들지 한번 생각해보도록 할까요.

 

처음이니 간단하게 CheckBox 아이템 렌더러를 만들도록 해봅시다.

 

일단 메인 어플리케이션을 만들어 놓습니다.

 

[code xml]
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
    <![CDATA[
    import mx.collections.ArrayCollection;

    [Bindable]
    private var ac:ArrayCollection = new ArrayCollection([
                                     {index:1, description:"test1", toggle:"Y"},
                                     {index:2, description:"test2", toggle:"N"},
                                     {index:3, description:"test3", toggle:"N"},
                                     {index:4, description:"test4", toggle:"Y"},
                                     {index:5, description:"test5", toggle:"N"},
                                     {index:6, description:"test6", toggle:"N"},
                                     {index:7, description:"test7", toggle:"Y"},
                                     {index:8, description:"test8", toggle:"N"},
                                     {index:9, description:"test9", toggle:"N"},
                                     {index:10, description:"test10", toggle:"N"},
                                     {index:11, description:"test11", toggle:"N"},
                                     {index:12, description:"test12", toggle:"N"}
                                     ]);
    ]]>
</mx:Script>
<mx:DataGrid dataProvider="{ac}">
    <mx:columns>
        <mx:DataGridColumn headerText="index" dataField="index" />
        <mx:DataGridColumn headerText="description" dataField="description" />
        <mx:DataGridColumn headerText="check" dataField="toggle" />
    </mx:columns>
</mx:DataGrid>
</mx:Application>
[/code]

 

위와같이 코딩하고 컴파일하게되면 아래와 같이 산출물이 나오게 됩니다.

 

 

그럼 우리는 ratio라고 적혀있는 곳에 ItemRenderer를 적용할겁니다.

 

CheckBox를 상속받아 작업할겁니다. 왜 "UIComponent 안쓰고!"라고 말씀하신다면

그 이유는 UIComponent를 상속받은 클래스이기도 하고 지난시간 말했던 IDataRenderer, IDropInListitemRenderer, IListItemRenderer 이렇게 세 인터페이스가 이미 구현되어있으며 우리가 원하는 아이템 렌더러는 CheckBox아이템 렌더러이기 때문이지요.

 

[code as3]
package classes.controls.renderers
{
//-----------------------------------------------------------------------------
//
//  Import
//
//-----------------------------------------------------------------------------
import mx.core.IDataRenderer;
import mx.controls.CheckBox;
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.IDropInListItemRenderer;
import mx.controls.listClasses.IListItemRenderer;
import mx.events.FlexEvent;
public class CheckBoxRenderer extends CheckBox
{
    //-----------------------------------------------------------------------------
    //
    //  Constructor
    //
    //-----------------------------------------------------------------------------
    /**
     *  Constructor.
     */
    public function CheckBoxRenderer()
    {
        super();
    }

    //-----------------------------------------------------------------------------
    //
    //  Variables
    //
    //-----------------------------------------------------------------------------
    /**
     *  @private
     *  데이터 변경 확인 플래그
     */
    private var dataChanged:Boolean = false;

    //-----------------------------------------------------------------------------
    //
    //  Override Methods
    //
    //-----------------------------------------------------------------------------
    /**
     *  @private
     */
    override protected function createChildren():void
    {
        super.createChildren();
    }

    /**
     *  @private
     */
    override protected function commitProperties():void
    {
        super.commitProperties();
    }

    /**
     *  @private
     */
    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
        super.updateDisplayList(unscaledWidth, unscaledHeight);
    }
}
}
[/code]

초벌로 createChildren, commitProperties, updateDisplayList도 확장해 두고 아이템 랜더러로 사용하기 위한 준비를 마쳤습니다.

그리고 메인 어플리케이션은 아래와 같이 수정하고 컴파일 합니다.

[code xml]
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
    <![CDATA[
    import mx.collections.ArrayCollection;

    [Bindable]
    private var ac:ArrayCollection = new ArrayCollection([
                                        {index:1, description:"test1", toggle:"Y"},
                                        {index:2, description:"test2", toggle:"N"},
                                        {index:3, description:"test3", toggle:"N"},
                                        {index:4, description:"test4", toggle:"Y"},
                                        {index:5, description:"test5", toggle:"N"},
                                        {index:6, description:"test6", toggle:"N"},
                                        {index:7, description:"test7", toggle:"Y"},
                                        {index:8, description:"test8", toggle:"N"},
                                        {index:9, description:"test9", toggle:"N"},
                                        {index:10, description:"test10", toggle:"N"},
                                        {index:11, description:"test11", toggle:"N"},
                                        {index:12, description:"test12", toggle:"N"}
                                        ]);
    ]]>
</mx:Script>
<mx:DataGrid dataProvider="{ac}">
    <mx:columns>
        <mx:DataGridColumn headerText="index" dataField="index" />
        <mx:DataGridColumn headerText="description" dataField="description" />
        <mx:DataGridColumn headerText="radio" dataField="toggle" itemRenderer="classes.controls.renderers.CheckBoxRenderer" />
    </mx:columns>
</mx:DataGrid>
</mx:Application>
[/code]

일단 아래와 같은 결과가 나오면 성공입니다.

 

CheckBox 가 "Y"냐 "N"이냐에 따라 selected가 처리되는 로직이 필요합니다. 그러기 위해서는 그 코드를 어디에 넣는것이 좋을지 고민해봐야하는데 아까도 말했듯 모든 속성처리는 commitProperties에서 하게 됩니다. commitProperties를 호출하기 위해서는 저번에 이야기 했었던 대로 invalidateProperties() 메서드를 호출하면 됩니다. 그렇다면 코드를 아래와 같이 수정합니다.

 

[code as3]
package classes.controls.renderers
{
//-----------------------------------------------------------------------------
//
//  Import
//
//-----------------------------------------------------------------------------
import mx.core.IDataRenderer;
import mx.controls.CheckBox;
import mx.controls.dataGridClasses.DataGridListData;
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.IDropInListItemRenderer;
import mx.controls.listClasses.IListItemRenderer;
import mx.events.FlexEvent;
public class CheckBoxRenderer extends CheckBox
{
    //-----------------------------------------------------------------------------
    //
    //  Constructor
    //
    //-----------------------------------------------------------------------------
    /**
     *  Constructor.
     */
    public function CheckBoxRenderer()
    {
        super();
    }

    //-----------------------------------------------------------------------------
    //
    //  Variables
    //
    //-----------------------------------------------------------------------------
    /**
     *  @private
     *  데이터 변경 확인 플래그
     */
    private var dataChanged:Boolean = false;
    //-----------------------------------------------------------------------------
    //
    //  Override Properties
    //
    //-----------------------------------------------------------------------------
    override public function set data(value:Object):void
    {
        super.data = value;

        dataChanged = true;

        invalidateProperties();
    }

    //-----------------------------------------------------------------------------
    //
    //  Override Methods
    //
    //-----------------------------------------------------------------------------
    /**
     *  @private
     */
    override protected function createChildren():void
    {
        super.createChildren();
    }

    /**
     *  @private
     */
    override protected function commitProperties():void
    {
        super.commitProperties();
        if(dataChanged)
        {
            dataChanged = false;
            updateProperties();
        }
    }

    /**
     *  @private
     */
    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
        super.updateDisplayList(unscaledWidth, unscaledHeight);
    }

    //-----------------------------------------------------------------------------
    //
    //  Methods
    //
    //-----------------------------------------------------------------------------
    /**
     *  @private
     *  CheckBox의 속성을 변경
     */
    private function updateProperties():void
    {
        if(listData.label == "Y")
        {
            selected = true;
        }
        else
        {
            selected = false;
        }

        invalidateDisplayList();
    }
}
}
[/code]

위와 같이 코딩하게 되면 아래와 같은 산출물이 나오게 됩니다.

 

 

썩 잘나오는거 같습니다. 몇가지 마음에 안 드는 것이 있는데 일단 CheckBox가 셀의 중앙에 갔으면 좋겠고 CheckBox를 클릭하고 휠을 돌려보면 Check가 풀려버리는 버그가 있다는 것이지요.

 

CheckBox를 중앙에 놓으려면 CheckBox의 너비를 알아야하는데 그것은 CheckBox는 기본적으로 14픽셀의 넓이를 갖습니다. 그것을 어떻게 아냐구요?

measuredWidth를 trace 해보면 간단하게 알 수 있습니다. 아, measuredWidth는 컨포넌트 객체의 기본 너비를 이야기 합니다.

 

위의 두가지 문제점을 해결하기 위해 아래와 같이 코드를 수정합니다.

 

[code as3]
package classes.controls.renderers
{
//-----------------------------------------------------------------------------
//
//  Import
//
//-----------------------------------------------------------------------------
import flash.events.MouseEvent;

import mx.core.IDataRenderer;
import mx.controls.CheckBox;
import mx.controls.dataGridClasses.DataGridListData;
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.IDropInListItemRenderer;
import mx.controls.listClasses.IListItemRenderer;
import mx.core.mx_internal;
import mx.events.FlexEvent;

use namespace mx_internal;

public class CheckBoxRenderer extends CheckBox
{
    //-----------------------------------------------------------------------------
    //
    //  Constructor
    //
    //-----------------------------------------------------------------------------
    /**
     *  Constructor.
     */
    public function CheckBoxRenderer()
    {
        super();
    }

    //-----------------------------------------------------------------------------
    //
    //  Variables
    //
    //-----------------------------------------------------------------------------
    /**
     *  @private
     *  데이터 변경 확인 플래그
     */
    private var dataChanged:Boolean = false;

    //-----------------------------------------------------------------------------
    //
    //  Override Properties
    //
    //-----------------------------------------------------------------------------
    override public function set data(value:Object):void
    {
        super.data = value;

        dataChanged = true;

        invalidateProperties();
    }

    //-----------------------------------------------------------------------------
    //
    //  Override Methods
    //
    //-----------------------------------------------------------------------------
    /**
     *  @private
     */
    override protected function createChildren():void
    {
        super.createChildren();
    }

    /**
     *  @private
     */
    override protected function commitProperties():void
    {
        super.commitProperties();
        if(dataChanged)
        {
            dataChanged = false;

            updateProperties();
        }
    }

    /**
     *  @private
     */
    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
        super.updateDisplayList(unscaledWidth, unscaledHeight);

        currentIcon.x = (unscaledWidth - currentIcon.width) / 2;
    }

    //-----------------------------------------------------------------------------
    //
    //  Methods
    //
    //-----------------------------------------------------------------------------
    /**
     *  @private
     *  CheckBox의 속성을 변경
     */
    private function updateProperties():void
    {
        if(listData.label == "Y")
        {
            selected = true;
        }
        else
        {
            selected = false;
        }

        invalidateDisplayList();
    }

    //-----------------------------------------------------------------------------
    //
    //  Override EventHandler
    //
    //-----------------------------------------------------------------------------
    /**
     *  @private
     */
    override protected function clickHandler(event:MouseEvent):void
    {
        super.clickHandler(event);

        //data[DataGridListData(listData).dataField] = selected ? "Y" : "N";

        if(selected)
        {
            data[DataGridListData(listData).dataField] = "Y";
        }
        else
        {
            data[DataGridListData(listData).dataField] = "N";
        }
    }
}
}
[/code]

 

currentIcon이라는 생소한 속성을 위에서 보실 수 있습니다. 그것은 체크박스에서 체크되는 아이콘을 지칭합니다. 그 currentIcon이란 녀석은 mx_internal이라는 접근자를 이용하고 있고 LanguageReference에는 표시되지 않고 있어서 그 존재를 모르는 사람이 많습니다.

예전에 저도 그것을 몰라서 HBox에다가 넣고 horizontalAlign="center"를 주는 바보같은 짓을 일삼았는데 이 사실을 알고 난뒤에 Container를 사용하지 않고 좀더 가볍게 itemRenderer를 만들어 낼 수 있었지요.


그래서 updateDisplayList() 메서드에서 좌표를 잡아서 셀의 중앙에 놓고 clickHandler라는 메서드를 확장하여 변경된 값을 data에 반영하게 합니다. 그렇게 되면 아래와 같은 결과물이 나오게 되지요.

 

 

오. 그럴싸하게 작동하는 것 같습니다만 몇가지 아쉬운점이 있습니다.

 

1. "Y", "N" 값으로만 작동하게 되어있다.

2. CheckBox를 클릭하면 뭔가 다른 행동을 하고 싶을땐 어떻게 하나.

 

이렇게 두가지 사항인데요.

이 부분에 대해서는 다음시간에 알아보도록 해요~


Posted by 1010
카테고리 없음2014. 6. 10. 15:02
반응형

<mx:TextArea id="ta"

      valueCommit="ta.verticalScrollPosition = ta.maxVerticalScrollPosition" />

 

이면 되네요.

단, change 이벤트가 아닌 valueCommit 를 사용하는 이유는,

change 이벤트는 사용자의 입력에 따른 text 변경에 대해서 발생되는 이벤트라서,

프로그램에서 ta.text += "~~~~ Log 내용"; 과 같은 방식으로 내용을 수정하게 되면 발생하지 않습니다.

valueCommit 은 API 를 한번 확인해보시면 아시겠지만, 값 자체의 변경에 대한 모든 경우에 발생을 하는군요.

 

그럼 다들 즐플렉스~하세요. ^^

출처 : http://blog.naver.com/PostView.nhn?blogId=ezmo01&logNo=110084670815

Posted by 1010
반응형

각 컨트롤 속성에서

 

Mouse.cursor = MouseCursor.ARROW; 이렇게 주면된다.

 

AUTO : 자동

ARROW : 화살표

BUTTON : 버튼 누를때 나오는 한손꾸락

HAND : 잡아당길때 나오는 손바닥

IBEAM : 텍스트 창에서 나오는 I모양

 

예) mouseOver="Mouse.cursor = MouseCursor.BUTTON;" 이렇게 하면 된다.

출처 : http://blog.naver.com/PostView.nhn?blogId=sehwanq&logNo=50091363895

Posted by 1010
반응형


위 처럼 컨트롤 포인트를 이용하여 사이즈를 조절하는 컴포넌트를 만들려면...

1. 우선 각각의 포인트가 되는 사각형을 만듭니다.

ControlPoint.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009
   xmlns:s="library://ns.adobe.com/flex/spark" 
   xmlns:mx="library://ns.adobe.com/flex/mx" 
   width="9" height="9">
 
 <s:Rect width="9" height="9">
  <s:fill>
   <s:SolidColor />
  </s:fill>
 </s:Rect>
</s:Group>


2. 그 다음 위 검정색 사각형을 위치시켜 놓을 컨트롤 박스(Group)를 만들고 위 사각형을 제 위치에 놓습니다.

<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009
   xmlns:s="library://ns.adobe.com/flex/spark" 
   xmlns:mx="library://ns.adobe.com/flex/mx"
   xmlns:control="org.jhl.control.*"
   width="100" height="100">
 
 <fx:Script>
  <![CDATA[
   private var _currentPointID:String;
   private var _startPointX:Number = 0;
   private var _startPointY:Number = 0;
   private var _x:Number;
   private var _y:Number;
   private var _width:Number;
   private var _height:Number;
   
   protected function mouseDownListener( event:MouseEvent ):void
   {
    _startPointX = this.parent.mouseX;
    _startPointY = this.parent.mouseY;
    _currentPointID = ControlPoint(event.target).id;
    _x = this.x;
    _y = this.y;
    _width = this.width;
    _height = this.height;
    
    this.systemManager.addEventListener(MouseEvent.MOUSE_MOVE, moveListener);
    this.systemManager.addEventListener(MouseEvent.MOUSE_UP, upListener);
   }
  
   
   protected function moveListener( event:MouseEvent ):void
   {
    var pointer:ControlPoint = event.target as ControlPoint;
    
    var dx:Number = this.parent.mouseX - _startPointX;
    var dy:Number = this.parent.mouseY - _startPointY;
    
    if( _currentPointID == "topLeft" )
    {
     this.x = _x + dx;
     this.width = _width - dx;
     this.y = _y + dy;
     this.height = _height - dy;
    }
    else if( _currentPointID == "topCenter" )
    {
     this.y = _y + dy;
     this.height = _height - dy;
    }
    else if( _currentPointID == "topRight" )
    {
     this.width = _width + dx;
     this.y = _y + dy;
     this.height = _height - dy;
    }
    else if( _currentPointID == "middleLeft" )
    {
     this.x = _x + dx;
     this.width = _width - dx;
    }
    else if( _currentPointID == "middleRight" )
    {
     this.width = _width + dx;
    }
    else if( _currentPointID == "bottomLeft" )
    {
     this.x = _x + dx;
     this.width = _width - dx;
     this.height = _height + dy;
    }
    else if( _currentPointID == "bottomCenter" )
    {
     this.height = _height + dy;
    }
    else if( _currentPointID == "bottomRight" )
    {
     this.width = _width + dx;
     this.height = _height + dy;
    }
    
    event.updateAfterEvent();
   }
   
   
   protected function upListener( event:MouseEvent ):void
   {
    this.systemManager.removeEventListener(MouseEvent.MOUSE_MOVE, moveListener);
    this.systemManager.removeEventListener(MouseEvent.MOUSE_UP, upListener);
   }


    override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void
   {
    super.updateDisplayList(unscaledWidth, unscaledHeight);
    
    var g:Graphics = this.graphics;
    
    g.clear();
    g.lineStyle(1, 0x000000);
    g.drawRect(0, 0, unscaledWidth, unscaledHeight);
   }
  ]]>
 </fx:Script>
  
 <control:ControlPoint id="topLeft" left="-4" top="-4" 
        mouseDown="mouseDownListener(event)" />
 <control:ControlPoint id="topCenter" top="-4" horizontalCenter="0"
        mouseDown="mouseDownListener(event)" />
 <control:ControlPoint id="topRight" right="-4" top="-4"
        mouseDown="mouseDownListener(event)" />
 
 <control:ControlPoint id="middleLeft" left="-4" verticalCenter="0"
        mouseDown="mouseDownListener(event)" />
 <control:ControlPoint id="middleRight" right="-4" verticalCenter="0"
        mouseDown="mouseDownListener(event)"/>
 
 <control:ControlPoint id="bottomLeft" left="-4" bottom="-5"
        mouseDown="mouseDownListener(event)" />
 <control:ControlPoint id="bottomCenter" bottom="-5" horizontalCenter="0"
        mouseDown="mouseDownListener(event)" />
 <control:ControlPoint id="bottomRight" right="-4" bottom="-5"
        mouseDown="mouseDownListener(event)" />
 
</s:Group>



이 컴포넌트를 이용하여 다음과 같은 결과물을 얻을 수 있습니다.


p.s. 하양감자님의 지적부분을 수정하여 업데이트 하였습니다.

Flex 4 배포버전 : 4.0.0.14159 [2010/03/21]
Flex 4 현재버전 : 4.1.0.16076 [2010/06/10]



어제 만든 컴포넌트를 활용하여 원을 컨트롤하는 컴포넌트를 만들어 볼려고 합니다.


위 그림 처럼 외부는 컨트롤하는 외곽선과 컨트롤 점들로 그리고 내부는 원, 여기서 원이 사각형 또는 다각형의 모양일 수도 있구요

이렇게 구성하기 위해 먼저 원을 그리면..

Circle.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009
   xmlns:s="library://ns.adobe.com/flex/spark" 
   xmlns:mx="library://ns.adobe.com/flex/mx"
   width="100" height="100">
 
 <fx:Script>
  <![CDATA[
   override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void
   {
    super.updateDisplayList(unscaledWidth, unscaledHeight);
    
    var g:Graphics = this.graphics;
    
    g.clear();
    g.lineStyle(1, 0x000000);
    g.beginFill(0xEEEEEE);
    g.drawEllipse(0, 0, unscaledWidth, unscaledHeight);
    g.endFill();
   }
  ]]>
 </fx:Script>
  
</s:Group>

그리고 어제 만든 컴포넌트를 원 컴포넌트에 추가하면...
(원 컴포넌트를 컨트롤 하기 위해 컨트롤 컴포넌트에 target 이라는 속성을 둬서 원 컴포넌트를 컨트롤 하게 합니다.)

Circle.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009
   xmlns:s="library://ns.adobe.com/flex/spark" 
   xmlns:mx="library://ns.adobe.com/flex/mx"
   xmlns:control="org.jhl.control.*"
   width="100" height="100">
 
 <fx:Script>
  <![CDATA[
   override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void
   {
    super.updateDisplayList(unscaledWidth, unscaledHeight);
    
    var g:Graphics = this.graphics;
    
    g.clear();
    g.lineStyle(1, 0x000000);
    g.beginFill(0xEEEEEE);
    g.drawEllipse(0, 0, unscaledWidth, unscaledHeight);
    g.endFill();
   }
  ]]>
 </fx:Script>

 <control:ControlBox target="{this}" width="{this.width}" height="{this.height}"/>

</s:Group>

그리고 아래는 수정된 컨트롤 컴포넌트...

ControlBox.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009
   xmlns:s="library://ns.adobe.com/flex/spark" 
   xmlns:mx="library://ns.adobe.com/flex/mx"
   xmlns:control="org.jhl.control.*"
   width="100" height="100" >
 
 <fx:Script>
  <![CDATA[
   import mx.core.UIComponent;
   private var _currentPointID:String;
   private var _startPointX:Number = 0;
   private var _startPointY:Number = 0;
   private var _x:Number;
   private var _y:Number;
   private var _width:Number;
   private var _height:Number;
   private var _target:UIComponent = null;
   
   
   public function set target( value:UIComponent ):void
   {
    _target = value;
   }


   protected function mouseDownListener( event:MouseEvent ):void
   {
    _startPointX = _target.parent.mouseX;
    _startPointY = _target.parent.mouseY;
    _currentPointID = ControlPoint(event.target).id;
    _x = _target.x;
    _y = _target.y;
    _width = _target.width;
    _height = _target.height;
    
    this.systemManager.addEventListener(MouseEvent.MOUSE_MOVE, moveListener);
    this.systemManager.addEventListener(MouseEvent.MOUSE_UP, upListener);
   }
  
   
   protected function moveListener( event:MouseEvent ):void
   {
    var dx:Number = _target.parent.mouseX - _startPointX;
    var dy:Number = _target.parent.mouseY - _startPointY;
    
    if( _currentPointID == "topLeft" )
    {
     _target.x = _x + dx;
     _target.width = _width - dx;
     _target.y = _y + dy;
     _target.height = _height - dy;
    }
    else if( _currentPointID == "topCenter" )
    {
     _target.y = _y + dy;
     _target.height = _height - dy;
    }
    else if( _currentPointID == "topRight" )
    {
     _target.width = _width + dx;
     _target.y = _y + dy;
     _target.height = _height - dy;
    }
    else if( _currentPointID == "middleLeft" )
    {
     _target.x = _x + dx;
     _target.width = _width - dx;
    }
    else if( _currentPointID == "middleRight" )
    {
     _target.width = _width + dx;
    }
    else if( _currentPointID == "bottomLeft" )
    {
     _target.x = _x + dx;
     _target.width = _width - dx;
     _target.height = _height + dy;
    }
    else if( _currentPointID == "bottomCenter" )
    {
     _target.height = _height + dy;
    }
    else if( _currentPointID == "bottomRight" )
    {
     _target.width = _width + dx;
     _target.height = _height + dy;
    }
    
    event.updateAfterEvent();
   }
   
   
   protected function upListener( event:MouseEvent ):void
   {
    this.systemManager.removeEventListener(MouseEvent.MOUSE_MOVE, moveListener);
    this.systemManager.removeEventListener(MouseEvent.MOUSE_UP, upListener);
   }
   
   
   override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void
   {
    super.updateDisplayList(unscaledWidth, unscaledHeight);
    
    var g:Graphics = this.graphics;
    
    g.clear();
    g.lineStyle(1, 0x000000);
    g.drawRect(0, 0, unscaledWidth, unscaledHeight);
   }
  ]]>
 </fx:Script>
  
 <control:ControlPoint id="topLeft" left="-4" top="-4" 
        mouseDown="mouseDownListener(event)" />
 <control:ControlPoint id="topCenter" top="-4" horizontalCenter="0"
        mouseDown="mouseDownListener(event)" />
 <control:ControlPoint id="topRight" right="-4" top="-4"
        mouseDown="mouseDownListener(event)" />
 
 <control:ControlPoint id="middleLeft" left="-4" verticalCenter="0"
        mouseDown="mouseDownListener(event)" />
 <control:ControlPoint id="middleRight" right="-4" verticalCenter="0"
        mouseDown="mouseDownListener(event)"/>
 
 <control:ControlPoint id="bottomLeft" left="-4" bottom="-5"
        mouseDown="mouseDownListener(event)" />
 <control:ControlPoint id="bottomCenter" bottom="-5" horizontalCenter="0"
        mouseDown="mouseDownListener(event)" />
 <control:ControlPoint id="bottomRight" right="-4" bottom="-5"
        mouseDown="mouseDownListener(event)" />
 
</s:Group>

아래는 위 코드의 결과물입니다.


Flex 4 배포버전 : 4.0.0.14159 [2010/03/21]
Flex 4 현재버전 : 4.1.0.16076 [2010/06/10]


컨트롤 포인트를 이용해서 그림을 컨트롤 할 수 있는 소스입니다.

Image.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009
   xmlns:s="library://ns.adobe.com/flex/spark" 
   xmlns:mx="library://ns.adobe.com/flex/mx"
   xmlns:control="org.jhl.control.*"
   width="64" height="64">
 
 <fx:Script>
  <![CDATA[
   override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void
   {
    super.updateDisplayList(unscaledWidth, unscaledHeight);
    
    img.width = unscaledWidth;
    img.height = unscaledHeight;
   }
  ]]>
 </fx:Script>
 
 <s:BitmapImage id="img" source="@Embed('assets/rss.png')" />
 
 <control:ControlBox target="{this}" width="{this.width}" height="{this.height}"/>
 
</s:Group>

 




ControlBox 는 전에 올린 내용을 참고하시면 됩니다.



Posted by 1010
56. Eclipse Etc.../Eclipse2014. 5. 30. 11:05
반응형

출처 : http://jungkun86.egloos.com/4139049


컴퓨터를 포맷하는 바람에, 이클립스를 새로 설치하면서 관련된 플러그인을 설치했다.

subversive 플러그인을 설치한 후 이클립스를 재기동하니, SVN connector를 선택하라는 팝업이 하나 뜬다.

무심하게 취소를 눌렀더니, SVN connector가 설치가 안되었다 --;

이클립스를 재기동하고, subversive 플러그인을 삭제후 재설치해도 SVN connector가 설치되지 않는다.


그렇다면 

http://community.polarion.com/projects/subversive/download/eclipse/3.0/juno-site/ 

에서 직접 설치할 수 있다.

Posted by 1010
98..Etc/jQuery2014. 5. 21. 15:46
반응형

출처 : http://jhoonslife.tistory.com/524


jqGrid postData 처리시 주의 사항 !


jqGrid 에서 postData 처리를 위해서는 javascript 객체 형식을 써야 합니다.


좋은예와 안좋은예를 보여드리겠습니다.


안좋은예


$("#grid").jqGrid({

  url:'url.do'

, datatype: 'json'

  , mtype: 'POST'

  , page : 1

  , rowNum : 10

postData : "param1="+$("#param1").val() + "&param2="+$("#param2").val()

...



좋은예

$("#grid").jqGrid({

  url:'url.do'

, datatype: 'json'

  , mtype: 'POST'

  , page : 1

  , rowNum : 10

        , postData : { 

jobReqId:$("#jobReqId").val(), 

srcWkptId:$("#selSrcWkptId").val() 

}...



두가지의 차이점은 
postData 를 full text 로 입력 했느냐, 자바스크립트 객체로 입력 했느냐 입니다.

어떻게 보면 두가지 모두 비슷한 결과를 나타내는것 같지만
전달되는 요청관계를 자세히 살펴보면 전혀 다른 결과가 나타납니다.

안좋은예의 요청 파라미터
param1=param1&param2=param2

좋은예의 요청 파라미터

param1=param1&param2=param2&_search=false&nd=1353343119211&rows=10&page=1



두가지의 차이를 아시겠나요?
안좋은예는 정말 postData 안에 있는 항목만 파라미터로 담겨서 넘어갑니다. jqGrid 의 다른 속성 컬럼들이 파라미터에 담기질 않습니다.
(이유는 잘 모르겠습니다. 아는사람 설명 좀 부탁해요.)
그래서 당연히 페이징이랑 검색이 자연스럽게 넘어가지 않았던 거죠~!

그렇지만 자바스크립트 객체로 입력해 놓은 좋은예는 postData 뿐만아니라 jqGrid 의 다른 여러가지 프로퍼티들도 함께 넘어갑니다 ( 페이징처리 시에 필요한 rows 와 page 까지 함께..)

그렇기 때문에 첫번째 페이지가 나오는건 같은데 페이징 처리가 먹히지 않는 것은 postData 처리를 잘못 하였기 때문입니다.

postData 는 반드시 full TEXT 가 아닌 자바스크립트 객체 형식으로 넘기셔야 하고, 검색이나 페이징 시에도 마찬가지 입니다.

reload 의 나쁜예

$("#"+gridId).setGridParam({

page : pageNum,

rowNum : rowNum,

postData : "param1="+$("#param1").val()+"&param2="+$("#param2").val()

}).trigger("reloadGrid");



reload 의 좋은예

$("#"+gridId).setGridParam({

page : pageNum,

rowNum : rowNum,

postData : {

param1:$("#param1").val(),

param2:$("#param2").val()

}

}).trigger("reloadGrid");


-끝


Posted by 1010
반응형

http://braincast.nl/samples/jsoneditor/

Posted by 1010
카테고리 없음2014. 4. 25. 14:46
반응형
출처 : http://infobot.tistory.com/58

  • DokuWiki는 다양한 문서를 저장하고 체계적으로 관리할 수 있는 위키이다. 


  • 자체 히스토리 기능으로 이전 버전으로 복원이 가능하며, 플러그인을 추가해서
    도쿠위키의 기능을 확장, 강화할 수 있다. 


  • 도쿠위키를 복잡한 설치과정 없이 바로 사용할 수 있는 것이 DokuWiki Stick이다.




 다운로드




  • 다운로드받은 dokuwikistick-2009-12-25c 의 압축을 푼다. 


  • DokuWikiStick 폴더의 mapache.exe 를 실행한다. 


  • 인터넷 브라우저를 열고 주소창에 http://localhost:8800 를 입력한다. 


  • 도쿠위키 페이지가 보이고 관리자 계정으로 로그인 후 관리 작업 - 환경 설정 관리에서 설정을 수정할 수 있다.
    [관리자 계정]
    - 사용자: admin
    - 패스워드: admin



  • 도쿠위키 스틱 문서 저장 경로:
    DokuWikiStick\dokuwiki\data\pages




 다운로드 링크


Posted by 1010
56. Eclipse Etc.../Eclipse2014. 4. 23. 18:36
반응형

org.springframework.beans.factory.BeanCreationException: Error creating bean with name'exceptionTransfer'

 

조치방법 : pom.xml 의내용을전부컨트롤 + x 후저장. 이클립스빌드가끝나면다시컨트롤 + v 저장빌드가끝나면실행.

 

출처 : http://enspring.tistory.com/51

Posted by 1010