WeakEvent 패턴

업데이트: 2007년 11월

일반적인 응용 프로그램에서는 이벤트 소스에 연결된 처리기가 처리기를 소스에 연결한 수신기 개체와 함께 삭제되지 않을 수 있습니다. 이로 인해 메모리 누수가 발생할 수 있습니다. WPF(Windows Presentation Foundation)에는 특정 이벤트에 전용 관리자 클래스를 제공하고 해당 이벤트의 수신기에 인터페이스를 구현함으로써 이러한 문제를 해결하는 데 사용할 수 있는 특수한 디자인 패턴이 도입되었습니다. 이 디자인 패턴을 WeakEvent 패턴이라고 합니다.

WeakEvent 패턴을 구현하는 이유

이벤트 수신은 메모리 누수를 야기할 수 있습니다. 일반적으로 이벤트는 소스의 이벤트에 처리기를 연결하는 언어별 구문을 사용하여 수신합니다. 예를 들어 C#에서는 이 구문이 source.SomeEvent += new SomeEventHandler(MyEventHandler)입니다.

이 방법을 사용하면 이벤트 소스에서 이벤트 수신기로의 강력한 참조가 만들어집니다. 일반적으로 수신기에 대한 이벤트 처리기를 연결하면 해당 수신기의 개체 수명이 소스의 개체 수명의 영향을 받게 됩니다. 단, 이벤트 처리기를 명시적으로 제거한 경우에는 예외입니다. 하지만 소스의 수명이 아니라 수신기가 응용 프로그램의 시각적 트리에 현재 속해 있는지 여부와 같은 다른 요소를 기준으로 수신기의 개체 수명을 제어하려는 경우가 있습니다. 소스의 개체 수명이 수신기의 개체 수명보다 긴 경우 일반적인 이벤트 패턴으로 인해 메모리 누수가 발생합니다. 즉, 수신기가 의도했던 것보다 오래 활성 상태를 유지합니다.

WeakEvent 패턴은 이러한 메모리 누수 문제를 해결할 수 있도록 디자인되었습니다. WeakEvent 패턴은 이벤트에 대해 수신기를 등록해야 하지만 수신기가 자신의 등록 취소 시기를 명시적으로 알지 못하는 경우 또는 소스의 개체 수명이 수신기의 "적절한" 개체 수명보다 긴 경우 사용할 수 있습니다. "적절한" 개체 수명을 정의하는 것은 개발자에 달려 있습니다. WeakEvent 패턴을 사용하면 수신기의 개체 수명 특성에 영향을 주지 않고 수신기가 이벤트에 대해 등록하여 이벤트를 수신할 수 있습니다. 수신기를 가비지 수집할 수 있는지 여부를 결정할 때는 소스로부터의 암시적 참조가 실제로는 어떤 영향도 주지 않습니다. 이러한 참조를 약한 참조라고 합니다. WeakEvent 패턴 및 관련 API도 이를 반영하여 이름이 지정되었습니다. 이 경우 수신기를 가비지 수집하거나 다른 방법을 통해 삭제할 수 있으며 소스는 삭제된 개체에 대한 수집할 수 없는 처리기 참조를 보유하지 않고도 계속 존재할 수 있습니다.

WeakEvent 패턴을 구현해야 하는 사용자

WeakEvent 패턴 구현은 주로 컨트롤 작성자와 관련된 기능입니다. 그 이유는 컨트롤의 동작과 제약, 컨트롤이 삽입된 응용 프로그램에 컨트롤이 미치는 영향을 다루는 사람이 주로 컨트롤 작성자이기 때문입니다. 여기에는 컨트롤의 개체 수명 동작과 앞에서 설명한 메모리 누수 문제에 대한 처리 등이 포함됩니다.

본질적으로 WeakEvent 패턴의 적용에 중점을 두는 시나리오도 있습니다. 한 예로, 소스 개체(데이터 소스)가 바인딩의 대상인 수신기 개체와 완전히 독립적인 데이터 바인딩 시나리오를 들 수 있습니다. WPF 데이터 바인딩의 대부분의 측면에는 이벤트 구현 방법에 있어 WeakEvent 패턴이 이미 적용되어 있습니다.

WeakEvent 패턴을 구현하는 방법

WeakEvent 패턴 구현은 다음 세 가지 측면으로 이루어집니다.

  • WeakEventManager 클래스에서 관리자를 파생합니다.

  • 소스에 대한 강력한 참조를 생성하지 않고, 약한 이벤트에 대한 수신기를 등록할 모든 클래스에 IWeakEventListener 인터페이스를 구현합니다.

  • 수신기를 등록할 때 수신기가 WeakEvent 패턴을 사용하도록 할 이벤트의 기본 add 및 remove 접근자를 사용하는 대신 이벤트에 대한 전용 WeakEventManager에서 "AddListener" 및 "RemoveListener" 구현을 사용합니다.

WeakEventManager

일반적으로 관리자 클래스는 WeakEvent 패턴을 구현하는 이벤트와 1:1 관계로 만듭니다. 예를 들어 Spin이라는 이벤트가 있으면 이 이벤트 전용의 약한 이벤트 관리자로 SpinEventManager 클래스를 파생합니다. 이 이벤트가 둘 이상의 소스 클래스에 있고 각 클래스에서의 동작이 전반적으로 동일하며 이벤트 데이터 형식을 공유하는 경우에는 각 이벤트에 동일한 관리자를 사용할 수 있습니다.

WeakEventManager 클래스에서 파생하기 위한 구현 검사 목록은 두 개의 가상 메서드를 재정의하는 것과 해당 이름이 가상 템플릿을 통해 관리되지는 않지만 존재해야 하는 몇 가지 다른 멤버를 노출하는 것으로 구성됩니다. 재정의는 WPF 인프라에서 이벤트 전달 모드를 시작하거나 종료하는 데 사용됩니다. 고유의 IWeakEventListener 구현이 WeakEventManager를 사용하여 이벤트에 수신기를 연결할 수 있도록 하려면 다른 멤버도 필요합니다.

WeakEventManager에서 파생하는 것과 관련된 자세한 구현 설명은 WeakEventManager 참조 항목의 "상속자 참고 사항"을 참조하십시오.

IWeakEventListener

IWeakEventListener 구현 클래스의 역할은 인터페이스 메서드 ReceiveWeakEvent를 구현하는 것입니다. ReceiveWeakEvent 구현은 해당 클래스에 존재하는 모든 이벤트 참조를 적절한 WeakEventManager로 전달하는 중앙 집중화된 구현이어야 합니다.

IWeakEventListener 인터페이스 구현에 대한 자세한 구현 설명은 ReceiveWeakEvent 메서드 참조 항목의 "구현자 참고 사항"을 참조하십시오.

수신기 연결

기본 이벤트인 ClockwiseSpin 이벤트(Spinner로 정의됨)를 사용한다고 가정해 보십시오. 이 이벤트에 WeakEvent 패턴을 사용하려면 WeakEventManager에서 파생된 기존 ClockwiseSpinEventManager 클래스를 사용하거나 클래스를 직접 구현합니다. SpinListener라는 수신기 클래스가 있는데 이를 수신기로 사용하려 한다고 가정해 보십시오. WeakEvent 패턴을 사용하는 대신 처리기를 연결하는 일반적인 방법을 따르는 경우 다음과 같이 += 구문을 사용합니다.

spinnerInstance.ClockwiseSpin += new EventHandler(MyOnCWSpinHandler);

하지만 IWeakEventListener를 구현하는 클래스가 있고 구현에 ClockwiseSpin 이벤트 및 해당 관리자를 포함해야 하는 경우에는 WeakEvent 패턴으로 대신 다음과 같은 구문을 사용합니다.

ClockwiseSpinEventManager.AddListener(spinnerInstance, this);

그런 다음 해당 이벤트에 대한 처리 논리를 기본 대리자 기반 처리기로 지정하는 대신 클래스에 구현한 ReceiveWeakEvent 중 하나에서 지정합니다.

외부 이벤트에 대해 WeakEvent 패턴 구현

WeakEvent 패턴이 지닌 한 가지 흥미로운 점은 코드베이스에 포함되지 않은 이벤트에 대해 이 패턴을 구현할 수 있다는 것입니다. 소스 관점에서는 처리기가 해당 이벤트에 연결되는 방법이 같으며 처리기가 WeakEventManager를 통해 제어됩니다. 이벤트에 대해 WeakEventManager를 정의한 다음 해당 이벤트를 수신하기 위해 이 패턴을 사용할 가능성이 있는 모든 수신기에서 이벤트를 ReceiveWeakEvent 논리의 일부로 포함하기만 하면 됩니다.

참고 항목

개념

라우트된 이벤트 개요

데이터 바인딩 개요

참조

WeakEventManager

IWeakEventListener