SWT에는 크게 두 종류의 클래스들이 있습니다. 하나는 UI를 추상화하는 클래스들로 이들은 모두 Widget을 상속받습니다. 나머지는 Resource를 상속받는 클래스들로 운영체제 GDI자원을 추상화 합니다.

Widget#dispose()

위짓을 디스포즈한다는 것은, 운영체에게 해당 UI의 핸들을 돌려주는 것을 의미합니다. 운영체제는 부모의 핸들만 반환 받으면, 그 핸들의 UI에 붙어 있던 자식들도 함께 회수하기 때문에, 일일히 자식까지 디스포즈 할 필요가 없습니다.

위짓의 디스포즈 절차

개발자 -> Composite : dispose()
Composite -> Composite : dispose evnet
Composite -> 자식들 : release()
자식들 -> 자식들 : dispose event
자식들 --> 개발자 :

위에서 보이는 것과 같이, 자식들의 dispose() 메서드는 호출되지 않고, 단지 dispose 이벤트만 발송됩니다. Canvas나 Composite를 상속받는 경우, 부모가 파기될 때 dispose() 메서드가 호출될 것이라 생각하고, GDI자원들을 dispose하는 코드들을 dispose()안에 작성해두면 릭이 생기니 주의가 필요합니다. 대신 다음과 같은 형태로 디스포즈 하여야 합니다.

addDisposeListener(new DisposeListener() {	
	@Override	
	public void widgetDisposed(DisposeEvent e) {
		disposeResource();  // 이 컨트롤이 사용하는 리소스들을 dispose하는 메서드
	}
});

Resource#dispose()

리소스는 UI인 위짓과 달리, 트리 형태의 부모 자식 구조가 없습니다. 이러한 GDI자원에는 컬러, 커서, 폰트, 그래픽 컨텍스트, 폴리곤, 패턴, 텍스트레이아웃, 어파인 트랜스폼등이 있습니다. 색상을 예로 들면, 빨강이라는 색깔은 여러 UI가 함께 공유해서 사용하고 있을 수 있습니다. 따라서, 리소스들은 반드시 개발자가 불필요한 시점을 판단하여 dispose()를 직접 호출 해 주어야 합니다.

JFace

JFace를 처음 시작하는 사람들은 JFace를 SWT의 일부로 착각하는 경우가 많습니다. 예를 들어 Window나 Dialog를 SWT의 Shell과 비슷한 것으로 생각하곤 합니다. 하지만 JFace는 SWT 위짓을 이용하여 원하는 UI를 대신 만들어주는 일종의 팩토리라고 볼 수 있습니다.

예를 들어 TreeViewer는 일일히 TreeItem을 만들고 아이콘, 텍스트를 지정하는 대신, 개발자가 공급한 컨텐트 프로바이더와 레이블 프로바이더를 이용하여 그 작업을 대신 해 줍니다. 또 다른 예를 들면 Action과 같은 추상화된 비즈니스 객체를 ToolbarManager는 알아서 Toolbar와 ToolItem을 만들고 Toolitem에 눌림 이벤트를 후킹해 비즈니스 로직과 연결해 줍니다.

마찬가지로 Window나 Dialog도 개발자가 Shell을 쉽게 만들수 있도록 도와주는 클래스이며, 어떤 SWT클래스도 상속받지 않습니다. 이렇게 UI를 쉽게 만들게 해 주는 것 이외에 JFace의 또 다른 임무는 컨트롤링 코드를 쉽게 만드는 것입니다.

예를들어 Person 객체의 name이 변경되어 해당 트리 아이템에 text를 다시 지정해야 하는 경우, SWT만으로 처리하려면, 개발자는 해당 모델을 표현하는 TreeItem의 맵을 직접만들고 관리하며, 찾아내어 일일히 setText를 호출해야 합니다. 이러한 저수준 UI코드가 컨트롤링 코드에 포함되면, 가독성이 크게 훼손되고 안전한 개발을 해 나가기 어렵습니다. TreeViewer는 이런 경우 간단하게 TreeViewer#update(Object element) 메서드를 제공하여 특정 모델을 표현하는 TreeItem을 간단하게 갱신할 수 있게 해 줍니다.

정리하면 JFace의 가장 중요한 두가지 역할은:

  • SWT를 이용하여 UI를 쉽게 구성할 수 있개 해줌
  • 컨트롤러 코드를 작성을 간단하게 만들어 줌
이라고 할 수 있습니다.

JFace의 소스들을 참고하여, 여러분이 만드는 에디터나, 복잡한 컨트롤들도 유사 패턴으로 작성해 보세요. 좋은 훈련이 됩니다.

JFace의 디스포즈 패턴

모든 JFace 클래스들이 그런 것은 아니지만, 대부분의 JFace 클래스들이 SWT를 이용하여 UI를 생성하고, 그것들을 쉽게 관리하게 하는 것을 기본으로 하고 있기 때문에, JFace 객체의 생명주기는 그들이 만들어 낸 UI와 대부분 일치합니다. JFace는 UI를 만들면서 보통 많은 GDI자원들을 함께 사용합니다. 따라서 JFace 스타일의 코드들은 UI의 dispose 이벤트를 받으면 사용되었던 모든 자원을 반환합니다.

TreeViewer의 디스포즈 예

Tree -> TreeViewer : dispose event
TreeViewer -> LabelProvider  : dispose
LabelProvider -> LabelProvider : 사용된이미지 및 색상 정리
LabelProvider -> TreeViewer : 
TreeViewer -> TreeViewer : unmap 
TreeViewer --> Tree :

결론

위 예제에서도 나타나는 것처럼 JFace의 요소인 LabelProvider는 dispose() 메서드를 갖고 있지만 LabelProvider는 위짓도 리소스도 아닙니다. dispose()의 의미도 전혀 다릅니다. 이들을 착각하게 되면, 릭이나 미궁에 빠지기 십상입니다.

다시 한번 정리하면 dispose()란:

  • SWT 위짓: 자기자신과 자기자신에 붙어있는 하위 위젯들 모두 Dispose 이벤트를 발송하게 한 뒤, 최초 dispose 메서드를 호출받은 위짓만 핸들을 운영체제 반환
  • SWT 리소스: 운영체제에게 빌려온 GDI 자원을 운영체제에게 반환
  • JFace: 자기 자신이 SWT를 이용하여 UI등을 구축하는데 사용했던 모든 자원을 반환 및 정리

Posted by 지이이이율
,