티스토리 뷰

가끔 이벤트 핸들러에 인자 값을 전달한다던지, 특정 횟수 만큼 호출되고 나면, 삭제를 해야 한다던지 할 경우가 있지요..
그런 이유로 다들 EventUtil 하나 정도는 가지고 계실 것 같은데요 ^^;

잉여 돋는 시간에 프레임워크를 만들고 있는데, 그 중 일부인 소스 공유합니다.
사용 방법은 주석에 써놨습니다~ ^^

다들 즐거운 하루 되세용~

package actionwire.util
{
	
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.utils.Dictionary;

/**
 * 
 * @author lovedev
 * @example 사용 예
 *  
 *  var sp:Sprite = new Sprite();
 *  EventProxy.addEventListener(sp, Event.SELECT_ALL, function($e:Event, $test:String):void
 *  {
 *	trace($test);
 *  },1,"testMessage AAA");
 *	
 *  var fn:Function = function($e:Event, $test:String):void
 *	{
 *		trace($test);
 *	};
 * 	
 *	EventProxy.addEventListener(sp, Event.SELECT_ALL, fn,0,"TestMessage BBB");
 *	
 *	sp.dispatchEvent(new Event(Event.SELECT_ALL));
 *	EventProxy.removeEventListener(sp, Event.SELECT_ALL, fn);
 *	sp.dispatchEvent(new Event(Event.SELECT_ALL));
 * 
 * //output
 * //testMessage AAA 
 * //TestMessage BBB
 * 
 * -----------------------------------------------------------------------------------
 * //이벤트 dispatch는 2번 되었지만
 * //테스트 메시지 AAA를 찍는 핸들러는 1회 호출 후 자동 remove 되었으며, 
 * //테스트 메시지 BBB를 찍는 핸들러는 강제 remove되어 핸들러가 호출되지 않는다.
 * -----------------------------------------------------------------------------------
 * 
 * 
 */	
public class EventProxy
{
	
	private static var _eventList:Dictionary = new Dictionary();
	
	/**
	 * 이벤트 발생이 가능한 객체에 이벤트를 등록합니다.
	 * @param $target 이벤트 발생 객체
	 * @param $eventType 이벤트 타입
	 * @param $listener 이벤트 핸들러
	 * @param $limitCount 
	 * 

호출 제한 카운트 지정 0일 경우 무제한 지정되었을 경우, * 지정된 수만큼 반복후 remove

* @param $rest 핸들러에 전달될 인자 * */ public static function addEventListener( $target:IEventDispatcher, $eventType:String, $listener:Function, $limitCount:uint = 0, ...$rest):void { if(!_eventList[$target]) _eventList[$target] = {}; if(!_eventList[$target][$eventType]) _eventList[$target][$eventType] = []; _eventList[$target][$eventType].push({ "listener" : $listener, "args" : $rest, "limitCount" : $limitCount, "executeCount" : 0 }); if(_eventList[$target][$eventType].length == 1) $target.addEventListener($eventType, onProxyDispatchEvent); } /** * * @param $e * */ private static function onProxyDispatchEvent($e:Event):void { var target:IEventDispatcher = $e.currentTarget as IEventDispatcher; var eventType:String = $e.type; if(_eventList[target].hasOwnProperty(eventType) && _eventList[target][eventType].length > 0) { var listener:Function, args:Array, limitCount:uint, idx:String, eventInfo:Object; var removableListener:Array = []; for(idx in _eventList[target][eventType]) { eventInfo = _eventList[target][eventType][idx]; listener = eventInfo.listener; args = eventInfo.args; if(!(args[0] is Event)) args.unshift($e); listener.apply(target, args); eventInfo.executeCount++; if(eventInfo.limitCount > 0 && eventInfo.limitCount <= eventInfo.executeCount) { removableListener.push(eventInfo); } } for each (eventInfo in removableListener) removeEventListener(target, eventType, eventInfo.listener); listener = null; args = null; idx = null; eventInfo = null; removableListener = null; } } /** * EventProxy를 통해 생성된 이벤트를 제거합니다. * @param $target * @param $eventType * @param $listener * */ public static function removeEventListener( $target:IEventDispatcher, $eventType:String = null, $listener:Function = null):void { //Case 1 if($eventType === null && $listener === null) { for(var eventType:String in _eventList[$target]) { $target.removeEventListener(eventType, onProxyDispatchEvent); } delete _eventList[$target]; return; } //Case 2 if($eventType !== null && $listener === null && _eventList[$target].hasOwnProperty($eventType)) { $target.removeEventListener($eventType, onProxyDispatchEvent); delete _eventList[$target][$eventType]; return; } //Case 3 if($eventType !== null && $listener !== null && _eventList[$target].hasOwnProperty($eventType)) { for(var idx:String in _eventList[$target][$eventType]) { if(_eventList[$target][$eventType][idx].listener === $listener) { _eventList[$target][$eventType][idx] = null; _eventList[$target][$eventType].splice(int(idx), 1); break; } } if(_eventList[$target][$eventType].length == 0) { $target.removeEventListener($eventType, onProxyDispatchEvent); delete _eventList[$target][$eventType]; } } } } }
저작자 표시 비영리 동일 조건 변경 허락
신고
댓글
  • 프로필사진 Favicon of http://diebuster.com BlogIcon hika 처음으로 댓글을 남겨봅니다(처음부터 이런 글이냐..)
    위에서 언급하신 것처럼 이벤트 관련으로 다들 비슷한 생각들을 해보셨을거라 대동소이한 소스를 갖고 있어요 ^^
    단지 개인적인 의견을 말씀드리자면(이제부터 시작..)
    매번 unshift로 이벤트를 삽입하는 건 좀 지양해야할 거 같고,
    실무상 rest 외에도 외부 파일로부터의 입력이나 기존 배열로부터의 입력도 잦기 때문에 배열을 인자로 받는 시그니처도 같이 제공해 주시면 더 좋을거 같고,
    hasown도 느리니까 안쓰고 걍 대괄호 두 번으로 대체했으면 좋겠고,
    길이체크도 리무브로직을 믿으신다면 안했으면 좋겠지만..
    이런 건 다 게임만드는 입장이라 기저프레임웍이 호출되는 횟수를 고려했을 때 성능에 대한 주관적인 견해이고,

    진짜 의견은..

    매번 커스텀 정보를 오브젝트를 생성해서 넣을거면 걍 실드클래스를 자료전용으로 선언해서 차라리 그 클래스의 인스턴스를 생성하고 디스패치시에도 해당 인스턴스의 메소드를 호출하도록 중앙 스태틱에서 알고리즘을 이사시킬 것과,
    이를 통해 배열을 각 딕셔너리의 컨테이너로 쓰시던걸 벡터로 전환하심이 어떨까하는거죠.

    결국 분명히 더 추가되거든요.

    지금 도입하신 리미트체크기능 외에도 수 많은 커스텀 확장 기능이....
    그 때마다 이벤트 처리기 본체를 건드리는 것보단 역시 개별 이벤트보관클래스를 수정하는 편이 안전하니까..^^

    머 개인적인 사견이었습니당.
    2011.02.18 12:06 신고
  • 프로필사진 Favicon of http://lovedev.tistory.com BlogIcon Kevin lovedev 와~~ 엄청난!! 댓글이어요~~

    이건 프레임워크의 일부 유틸이에요. ^^
    잉여 시간에 만든 건데. 이것의 포인트는

    dispatch를 Reflection시킨다기 보다는
    eventType이 같을 경우 addEventListener는 1번밖에 안된다는 것이겠지요 ^^; 물론 이벤트 전파 과정에 대한 생리 주기를 제한적으로 타는 것이 단점이에요.

    priority같은 우선 순위나 버블 개념이겠지요..

    이 외에 Vector로 할 경우, Key 인덱싱 시간이 더 들 것 같습니다. 배열의 단점이잖아요.. 또한 9기준으로 했을 때, 호환성 측면에서는 Vector는 안쓰는게;;

    말씀하신 것처럼, Custom Object보다 클래스로 가는게 빠르고 좋아요~ ^^)/ 맞아요~

    그냥 30분 정도 생각하면서 프로토타입 삼아 만든거라.. 세밀한 부분까지 적용하지 않은 것으로 받아 들여 주세오.. 무섭네요~ ^^;;;

    rest외 배열로 인자를 받는 거 좋네요 ^^);;

    Dictionary로 리스트를 잡은 것은 외부에서 GC되어 객체가 사라질 경우, 이벤트의 키값도 저절로 사라지도록 한 거라 봐주셔요~
    Object를 키로 삼기 좋아서요.. 물론 Dictionary객체가 느리긴 하죠. ^^

    첨부적으로 말씀 드리지만,
    이벤트로직에 의한 통신으로 프레임워크는 움직이지 않습니다. 프레임워크는 프로토콜을 설계해야지요~

    이벤트로 커뮤니케이션을 제어하는 것은 느리고 한계도 많아요..

    이벤트보다 더 LOW레벨에서 지정된 프로토콜로 통신을 하게 됩니다.

    그냥.. 간단히 프레임워크에서 이벤트를 써야할 때, Refelection하려고 유틸성으로 넣은 거에요~

    사려깊은 댓글 무한 감사드립니다!!
    2011.02.18 10:44 신고
  • 프로필사진 Favicon of http://diebuster.com BlogIcon hika if(!_eventList[$target][$eventType]){
    _eventList[$target][$eventType] = [];
    벡터를 도입하자는건 이부분의 배열로 중앙컨테이너인 딕셔너리를 대체하자는건 아니었습니다 ^^;

    참고로 혹시 각 인스턴스가 브레이크로 찍어보면 @@로 시작하는 유일한 식별키를 갖고 있잖아요. 그것좀 추출하는 메서드가 있을까요 ^^; 모든 as3인의 열망 객체 id얻기를 성공시켰나 궁금합니다.
    2011.02.18 12:11 신고
  • 프로필사진 Favicon of http://lovedev.tistory.com BlogIcon Kevin lovedev 프로파일러는 만들어 봤는데.. 객체의 고유 아이디 찾아 본다는 생각은 안해봤네요..

    Sampler로 얻어진 ID가 객체의 원래 아이디로 알고 있어서 이 부분을 테스트 해봤더니.. 아니더군요.

    그래서 수단과 방법을 가리지 않고 아이디를 알아내긴 했는데요..

    근데 이게 왜 필요한건지가 좀 ^^;;
    객체 비교할 때 속도 측면?
    바보 된거 같아여;;;

    어디에 필요한지 알려 주시면~ 포스팅 하겠습니다. ^^;(용도가 궁금해서요;;;)
    정답은 아니겠지만.. 찾아지긴 하네요..
    2011.02.18 15:30 신고
  • 프로필사진 Favicon of http://diebuster.com BlogIcon hika 원래 llvm이 가상머신에 개별 힙메모리 에뮬레이션으로 객체를 올릴때 숫자로 고유한 키를 부여해요. 자바에서는 최상위의 Object에 hash를 얻을수 있게 되어있잖아요.
    만약 디버깅시에 찍히는 고유한 인스턴스 id를 알 수 있으면 객체를 키로 잡는 컨테이너로 더이상 Dictionary를 쓸 필요가 없이 배열이나 벡터등의 숫자 인덱스 기반으로 이사갈 수 있게 되잖아요. 그 외에도 인스턴스 자체를 식별한 전역 키가 있으니까 객체 레퍼런스를 인자로 보내지 않고 프리머티브를 보낼 수도 있죠. 예를들어

    function setPosition( $target:DisplayObject, $x:Number, $y:Number ):void

    이런 함수를 생각해보면 만약 인스턴스 고유키를 알아낼 수 있다면,

    function setPosition( $targetId:String, $x:Number, $y:Number ):void{
    var target:DisplayObject = getObjectById( $targetId );
    ...
    이렇게 바꿀 수 있으니까 외부 설정으로 빼기도 좋고, 의존성도 현저하게 낮아지는 효과가...

    물론 Dictionary의 경우도 대부분 이용목적이 객체 자체를 키로 잡기 위해서인데 이게 결국 내부적으로는 객체의 해쉬키를 문자열로 해서 키로잡도록 어셈블리되거든요.

    var s:Shape = new Shape
    var d:Dictionary = new Dictionary;
    d[s] = 3;
    이렇게 되면 객체키를 잡는건 유저가 그렇게 느끼는거고, 사실은 s의 해쉬키를 문자열로 해서 Dictionary가 잡아준거잖아요. 짜피 고유한 숫자아이디를 얻을 수 있다면


    var s:Shape = new Shape
    var v:Vector.<Shape> = new Vector.<Shape>();
    v[getHashKey( s )] = 3;

    이렇게 바꿀 수 있으니까..일단 객체키를 잡으면 그게 안되는 언어도 많아서 크로스하게 소스를 쓸 수도 없고 나쁘다는..
    2011.02.18 16:55 신고
  • 프로필사진 Favicon of http://lovedev.tistory.com BlogIcon Kevin lovedev 글 올렸습니다~ ^^ 2011.02.19 20:17 신고
  • 프로필사진 Favicon of http://sewonist.com BlogIcon sewonist 우오~ -0- 이렇게 이벤트 관리하는 법도 있군요. 라고 리플 달려고 보니 이건 뭐... 신들의 대화;;;;;;
    글 하나하나 모두 엄청 나네요. 본문의 소스를 30분 만에 작성하신 lovedev 님 도 정말 대단하십니다. 역시 ACC ^^
    구현 방법적인 면은 계속 디벨롭 될 수 있겠죠. 그런걸 떠나서 아이디어 자체가 너무 신기합니다.
    생각도 못해봤다는.. 큰 영감 받고 갑니다.
    2011.02.19 09:59 신고
  • 프로필사진 Favicon of http://lovedev.tistory.com BlogIcon Kevin lovedev 네네 그대로 쓰지 말고!! 보완해서 쓰셔요!! 그래야 발전의 발전을 거듭할 수 있고, 공개하는 의미도 있을 것 같아요! 좋게 봐주셔서 그저 감사할 따름이어요~ ^^;

    개선하면 트랙백이라도 걸어 주세요~ ^^
    감사합니다!
    2011.02.19 20:11 신고
댓글쓰기 폼