Creative Wrong Answer


데이터를 표시 하다보면 어쩔수 없이 버튼등으로 처리 하지 못하고 Text나 Label 등을 사용해서 보여줘야 하는 경우가 생긴다.

Text 데이터를 보여 주면서 외부 링크의 경우 htmlText에 직접 링크를 걸어주면 되지만 플렉스 내부에서 처리 해야 하는 경우에는 좀 곤란해지기도 하는데 이럴때 TextEvent로 htmlText의 링크를 체크 할수 있다.

TextEvent.LINK 와 TextEvent.TEXT_INPUT 두개밖에 없고 매우 간단하다

TextEvent () 생성자
public function TextEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false, text:String = "")

언어 버전:  ActionScript 3.0
런타임 버전:  AIR 1.0, Flash Player 9

텍스트 이벤트에 대한 정보가 포함된 Event 객체를 만듭니다. Event 객체는 매개 변수로 이벤트 리스너에 전달됩니다.

매개 변수
type:String — 이벤트 유형입니다. 이벤트 리스너는 상속된 type 속성을 통해 이 정보에 액세스할 수 있습니다. 사용할 수 있는 값은TextEvent.LINK 및 TextEvent.TEXT_INPUT입니다.
 
bubbles:Boolean (default = false) — Event 객체가 이벤트 흐름의 버블링 단계에 참여하는지 여부를 결정합니다. 이벤트 리스너는 상속된 bubbles속성을 통해 이 정보에 액세스할 수 있습니다.
 
cancelable:Boolean (default = false) — Event 객체를 취소할 수 있는지 여부를 결정합니다. 이벤트 리스너는 상속된 cancelable 속성을 통해 이 정보에 액세스할 수 있습니다.
 
text:String (default = "") — 사용자가 입력한 한 자 이상의 텍스트 문자입니다. 이벤트 리스너는 text 속성을 통해 이 정보에 액세스할 수 있습니다.


예제를 보면 첫번째 Google 링크는 외부 링크로 열리고 네이버 링크는 Flex 내부에서 처리해서 Alert을 띄운다.

[어플리케이션 스크립트소스]

private var text:TextLink;

private function init():void
{
 text = new TextLink();
 
 text.htmlText = "Google = http://google.com
Naver = http://naver.com"; text.addEventListener(TextEvent.LINK, checkLink); addChild(text); text.x = 100; text.y = 80; } private function checkLink(e:TextEvent):void { Alert.show(e.text+" 링크를 클릭했어요"); }

TextLink.as

package
{
 import flash.text.StyleSheet;
 import mx.controls.Text;

 public class TextLink extends Text
 {
  public function TextLink()
  {
   super();
  }
  
  override protected function createChildren():void
  {
   super.createChildren();
   
   var style:StyleSheet = new StyleSheet();
            style.setStyle("a:link", {color:"#0000ff", textDecoration:"underline"});
            style.setStyle("a:hover", {color:"#00ff00", textDecoration:"none"});
            style.setStyle("a:active", {color:"#0000ff", textDecoration:"underline"});
            textField.styleSheet = style;
  }
 }
}



TextLInk.as는 링크 컬러를 설정하기 위해서 Text를 상속받아서 만든 컴포넌트이다. Label, Text, TextArea, TextInput 등등 TextField를 가지고 있는 놈은 어떤것을 상속받더라도 상관없다.

저작자 표시 비영리 동일 조건 변경 허락
신고

Comment 0


이 글은 자꾸 까먹어서 정리겸 이해하고 있는 수준에 맞게 풀어서 쓴 글이다..
-------------------------

Flash나 Flex의 메모리는 개발자가 그냥 지울수가 없게 되어있다.
자바도 마찬가지고 VM기반의 언어에서는 메모리를 할당하고 해제 하는 과정이 시스템에서 알아서 하도록 되어있기 때문에.. 언제 메모리가 해제 되는지 개발자가 컨트롤 할수가 없다.

개발을 한 프로그램을 돌리기 시작했는데.. 메모리가 사용할수록 증가 한다면 그 프로그램은 결국에 가서는 시스템에 문제를 일으키고 종료될 것이다.

메모리 관리는 Garbage Collector (이하 GC) 라는 놈이 하게 되는데 말그대로 쓰레기를 수거하는 역할이다.
더이상 프로그램에서 사용하지 않는 객체들을 초기화하고 메모리를 시스템으로 반환해주게 된다.

플레시 플레이어는 객체가 생성되면 시스템에 메모리를 요청하게 되는데 이때 이후에 생길 객체들을 위해서 객체의 메모리보다 더 많은 양을 할당 받게 된다.
이 메모리가 부족해지면 다시 시스템에 더 많은 메모리를 요구 하게 되는데 이때 GC 가 일을 하게 된다. 쓸모 없는게 있는지 찾고 있으면 지우고 부족분에 대한 메모리를 요청하게 되는것이다.

개발자가 removeChild 나 null 을 선언할때 메모리가 해제되지 않는다는것이 중요하다.

GC 가 돌면서 사용하지 않는 것을 찾는 방법은 두가지 룰이 있다.

1. 레퍼런스 카운팅 (Reference Counting)
2. 마크 앤 스윕 (Mark and Sweep)


레퍼런스 카운팅은 해당 객체가 참조하고 있거나 해당 객체를 참조하고 있는 놈이 있는지 찾는다. 해당 객체를 참조하고 있는 애들을 찾아서 있으면 카운트를 1 올리는 방식이다.

전부 찾아보고 나서 카운트가 0 인 객체들을 메모리에서 해제한다.

함수에서 Label 을 만든다

var label:Label = new Label();


new 키워드로 인스턴스를 생성했으니 메모리 공간을 할당 받는다. 하지만 참조하고 있는 곳이 없다. 따라서 GC 가 실행되면 label 은 메모리에서 사라진다.

라벨을 만들고 어플리케이션에 추가한다.

var label:Label = new Label();this.addChild(label);


함수 안에서 만들어졌지만 어플리케이션에 추가 되었다.

어플리케이션은 child 로 label 을 참조하고 있고, label은 parent로 어플리케이션을 참조하게 된다.

this.removeChild(label);


하게 되면 참조 관계가 사라지고 레퍼런스 카운팅 값이 0 이 되어 사라질수 있게 된다.

어플리케이션안에 캔버스를 만들고 캔버스 안에 라벨을 만든다.

var canvas:Canvas = new Canvas();this.addChild(canvas);var label:Label = new Label();canvas.addChild(label);


어플리케이션이 child로 캔버스를 참조하고 있고 label은 parent 로 참조하고 있으니 캔버스의 레퍼런스 카운팅은 2 이다.
라벨은 캔버스와 관계가 있으니 카운팅은 1 이된다.

이때 캔버스를 삭제한다.

this.removeChild(canvas);


어플리케이션에서는 캔버스가 사라지고 아무것도 없다. 과연 캔버스 객체는 메모리에서 사라질까?
화면에서 사라졌지만.  캔버스와 라벨은 여전히 레퍼런스 카운팅 값이 1 이다. 상호참조 하고 있기 때문에 레퍼런스 카운팅 기법만으로는 여전히 삭제가 되지 않는다.

이러한 상호참조 문제를 해결하기 위해서 사용되는 방법이 두번째의 마크 앤 스윕이다.

마크 앤 스윕 기법은 어플리케이션에서부터 하위로 참조되고 있는 객체들을 찾아서 마크한다. 어플리케이션에서 부터 참조를 체크 한다는 것이 중요하다.
쭉 마크를 하고 모든 뎁스에 대해서 마크가 끝나면 마크가 없는 애들은 지운다.
마크 앤 스윕 방법은 레퍼런스 카운팅 보다 작업하는 시간이 더 걸리고 시스템에 부하도 많이 주기 때문에 자주 실행되지는 않는다고 한다.

위의 캔버스는 어플리케이션에서 참조되고 있지 않기 때문에 메모리를 반환하고 생을 마감하게 된다.

중요한 것은 위의 방법을 이해하고 사용하지 않는 것을 삭제 할때에 다음번 GC가 삭제 된것을 전부 쓸어갈수 있도록 코딩을 해야 메모리가 무한정 증가하다가 뻗는것을 막을수 있다.

그럼 메모리를 반환하는데 있어서 걸림돌이 되는것들이 무엇이 있을까.
가장 문제가 되는 것이 이벤트 리스너다.

객체지향으로 설계 하다보니 이벤트를 엄청나게 사용하게 되고 addEventListener가 컴포넌트를 만들다 보면 대여섯개씩 기본으로 붙는 경우가 허다하다.

이벤트는 기본적으로 이벤트를 디스패치한 객체의 참조를 가지고 날아가게 된다.
따라서 event.target 또는 currentTarget 으로 이벤트를 발생시킨 놈을 사용할 수 있는것이다.

어떤 컴포넌트가 이벤트를 리슨 하거나 디스패치 하게되면 그 객체는 레퍼런스 카운팅이 1이상으로 유지 되기 때문에 살아있는 이벤트가 하나라도 있으면 삭제가 되지 않는 문제가 발생한다.

따라서 addEventListener 해준것은 반드시 removeEventListener 해주는 습관을 들여야 한다.

지워지는 시점이 명확하지 않다거나 내부에서 참조되서 관리가 힘들다거나 하는 경우에는 언제 remove 해줘야 하는지 결정하기가 쉽지 않다.
이럴때 사용하는것이 useWeakReference 이다.

addEventListener (type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
리스너에서 이벤트 알림을 받을 수 있도록 EventDispatcher 객체에 이벤트 리스너 객체를 등록합니다.

다섯번째 파라미터를 true 로 해주면 참조값을 약하게 잡고 있게 되고 이후에 GC 에서 삭제해줄 확률이 높아진다. 확률만 높아진다는 것이지 꼭 사라진다는건 아니기 때문에. removeEventListener 해주는 것이 가장 좋다.

이벤트쪽만 정리가 잘 되어있어도 메모리누수를 상당부분 막을수 있고.

플렉스IDE 를 사용한다면 개발중간중간에 프로파일러를 돌려서 체크 해보는 습관을 갖는게 좋을것같다.

나같은 경우는 커스텀 컴포넌트를 만들게 되면 destory() 함수를 만들어서 그 컴포넌트에서 사용되었던 이벤트 리스너를 일괄로 삭제하고 가능하면 컴포넌트 내부에 addChild 되어있던 것들도 삭제 해주는 함수를 만들어서 사용하고 있다.
removeChild 하기 전에 destory()를 실행시켜주고 삭제하면 기본적인 방지책은 되는것 같다.

PS. 메모리 관리에 좋은 방법들이 있으면 공유를 부탁드립니다~ 댓글 트랙백 환영
저작자 표시 비영리 동일 조건 변경 허락
신고

Comment +3

package {
	import flash.display.Bitmap;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.net.URLRequest;
	
	[SWF(width="1000", height="1000", frameRate="24", backgroundColor="#FFFFFF")]
	
	public class bitmapDataHandle extends Sprite
	{
		private var container:Sprite;
		private var bitmap:Bitmap;
		private var loader:Loader;
		public function bitmapDataHandle()
		{
			loader = new Loader();
			loader.load(new URLRequest("cat.jpg"));
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadHandler);
		}
		
		private function loadHandler(e:Event):void
		{
			loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadHandler);
			bitmap = loader.content as Bitmap;
			loader.unload();
			
			container = new Sprite();
			container.addChild(bitmap);
			container.addEventListener(MouseEvent.CLICK, clickHandler);
			addChild(container);
			
		}
		
		private function clickHandler(e:MouseEvent):void
		{
			var pixelValue:uint = bitmap.bitmapData.getPixel(mouseX,mouseY);
			trace("x : "+mouseX+" y: "+mouseY);
			trace("선택한 곳의 색상값 : "+pixelValue.toString(16));
		}
	}
}

까페에 질문글이 올라와서 급조한 테스트..

bitmapData에는 pixel을 이용해서 여러가지 작업을 할수 있다. 그중에 getPixel은 선택한 곳의 해당 픽셀값을 가져온다.

 

getPixels () 메서드  

public function getPixels(rect:Rectangle):ByteArray
언어 버전: ActionScript 3.0
런타임 버전: AIR 1.0, Flash Player 9

픽셀 데이터의 사각형 영역에서 바이트 배열을 생성합니다. 픽셀별로 부호 없는 정수(곱하지 않은 32비트 픽셀 값)를 바이트 배열에 씁니다.

매개 변수

rect:Rectangle — 현재 BitmapData 객체의 사각형 영역입니다.

반환값
ByteArray — 해당 Rectangle의 픽셀을 나타내는 ByteArray입니다.

오류
TypeError — rect가 null입니다.
getPixels () 메서드  
public function getPixels(rect:Rectangle):ByteArray
언어 버전: ActionScript 3.0
런타임 버전: AIR 1.0, Flash Player 9

픽셀 데이터의 사각형 영역에서 바이트 배열을 생성합니다. 픽셀별로 부호 없는 정수(곱하지 않은 32비트 픽셀 값)를 바이트 배열에 씁니다.

매개 변수

rect:Rectangle — 현재 BitmapData 객체의 사각형 영역입니다.

반환값
ByteArray — 해당 Rectangle의 픽셀을 나타내는 ByteArray입니다.

오류
TypeError — rect가 null입니다.

 

테스트 함수 만들고 포토샵에서 나오는 컬러 코드와 비교 해봤을때.

 

x : 530 y: 376
선택한 곳의 색상값 : 584b42

 

이렇게 출력된 좌표의 색상값을 포토샵에서 찍어보면 594b42 로 나온다.

 

몇군데 좌표를 테스트 해봤지만 포토샵과 똑같이 나오지는 않는것 같다.

하지만 사람눈으로 구별할수 있을만큼의 차이는 나지 않는다.

신고

Comment +2

웹이나 디비에서 데이터를 가져온 텍스트의 경우 저장하는 방법에 따라서 개행이 /r/n 으로 넘어오는 경우가 있다.

 

이 경우 플레시는 한줄 개행이 아니고 두줄로 처리 해서 텍스트 필드의 라인간격이 두배로 나오는 경우가 생기게 되는데 이때는 넘어오는 텍스트에서 /r을 검색해서 삭제 해버리면 된다.

 

이럴때 사용하는 함수..

private function removeCarriageReturn(str:String):String
{
	var myPattern:RegExp = /r/g;
	str = str.replace(myPattern,"");
	return str;
}

함수이름이 내용보다 긴것 같지만 무슨 상관이랴~

 

/r 은 캐리지리턴이라부르고 입력커서를 라인의 맨 앞으로 보낸다

/n은 라인피드라고 해서 커서를 다음줄로 내리게 된다.

 

 예전에는 두개가 모여야 한줄의 개행이었던걸로 알고 있는데 요즘에는 걍 n만 쓰면 대부분이 정상적으로 라인이 바뀐다.

신고

Comment 0