이벤트 리스너를 약한 참조로 유지해야합니까?


9

일반적으로 이벤트 리스너는 등록한 객체보다 오래 지속되지 않아야합니다.

이벤트 리스너가 기본적으로 약한 참조로 유지되어야 함을 의미합니까 (오브젝트 리스너가 약한 콜렉션에 저장함)?

청취자가 제작자보다 오래 살아야하는 유효한 사례가 있습니까?

아니면 그와 같은 상황은 실수이며 허용해서는 안됩니까?


약한 참조는 일반적으로 인스턴스로 표시되며 이러한 인스턴스는 가비지로 수집해야하는 시점까지 누적 될 수도 있습니다. 따라서 무료 점심이 아닙니다. 약한 참조를 지우는 동일한 논리가 강력한 참조를 지우는 경우가 있습니다.
Frank Hileman

답변:


7

왜 이벤트 리스너가 리스너를 등록한 객체보다 오래 지속되지 않아야합니까? 이벤트 리스너가 컨트롤의 메소드 (GUI 예제를 사용하는 경우)에 의해 등록되어야한다고 가정하는 것 같습니다.보다 정확하게는 GUI 툴킷의 컨트롤을 상속하는 클래스의 객체에 의한 메소드입니다. 예를 들어 이벤트 리스너를 등록하기 위해 특수 객체를 사용하고 나중에 해당 객체를 버릴 수 있습니다.

또한 이벤트 리스너가 약하게 참조 된 경우 해당 참조를 사용하지 않더라도 실제로 참조를 유지해야합니다. 그렇게하지 않으면 리스너가 임의의 시간에 수집됩니다. 그래서 우리는 버그를 얻습니다.

  • 실수로 쉽게 만들 수 있습니다 (사용하지 않을 참조 변수에 객체를 저장하는 것을 잊기 만하면됩니다).
  • 알아 채기 어렵습니다 (GC가 해당 개체를 수집하는 경우에만 해당 버그가 발생 함).
  • 디버그하기 어려움 (항상 릴리스 세션처럼 작동하는 디버그 세션에서 GC가 객체를 수집 한 경우에만 해당 버그가 발생 함)

그 버그를 피하는 것이 인센티브가 충분하지 않다면 여기에 더 있습니다.

  1. 생성 한 각 리스너 의 이름생각 해야합니다 .

  2. 일부 언어는 정적 분석을 사용하여 작성되지 않거나 읽지 않는 개인 구성원 필드가있는 경우 경고를 생성합니다. 이를 재정의하는 메커니즘을 사용해야합니다.

  3. 이벤트 리스너는 무언가를 수행하고 강력한 참조를 가진 객체가 수집되면 그 일을 중단합니다. 이제 프로그램 상태에 영향을 미치고 GC에 의존하는 것이 있습니다. 즉, GC가 프로그램의 구체적인 상태에 영향을 미칩니다. 그리고 이것은 나쁘다 !

  4. 약한 참조는 다른 수준의 간접 참조가 있고 참조가 수집되었는지 확인해야하기 때문에 속도가 느립니다. 참조가 약한 이벤트 리스너가 필요한 경우에는 문제가되지 않지만 그렇지 않습니다!


5

일반적으로 약한 참조가 사용되어야합니다. 그러나 먼저 "이벤트 리스너"의 의미에 대해 분명히해야합니다.

콜백

일부 프로그래밍 스타일, 특히 비동기 작업과 관련하여 특정 이벤트에서 실행되는 콜백으로 계산의 일부를 나타내는 것이 일반적입니다. 예를 들어, Promise[ 1 ]은 then이전 단계가 완료되면 콜백을 등록하는 메소드를 가질 수 있습니다 .

promise =
    Promise.new(async_task)                # - kick off a task
    .then(value => operation_on(value))    # - queue other operations
    .then(value => other_operation(value)) #   that get executed on completion
... # do other stuff in the meanwhile
# later:
result = promise.value # block for the result

여기서 then약속 (이벤트 소스)이 콜백에 대한 참조를 보유하는 유일한 객체이므로 여기에 등록 된 콜백 은 강력한 참조로 유지되어야합니다. 약속 자체의 수명이 제한되어 있으므로 약속 체인이 완료된 후 가비지 수집됩니다.

관찰자 패턴

관찰자 패턴에서, 대상은 종속 관찰자 목록을 가지고 있습니다. 피험자가 어떤 상태에 들어갔을 때, 일부 인터페이스에 따라 관찰자에게 알립니다. 관찰자는 대상에 추가 및 제거 될 수 있습니다. 이러한 관찰자는 의미 적 진공 상태에 존재하지 않지만 어떤 목적으로 이벤트를 기다리고 있습니다.

이 목적이 더 이상 존재하지 않으면 관찰자를 대상에서 제거해야합니다. 가비지 수집 언어에서도이 제거는 수동으로 수행해야 할 수 있습니다. 관찰자를 제거하지 못하면 대상에서 관찰자에 대한 참조를 통해 관찰자가 참조하는 모든 객체와 함께 유지됩니다. 이것은 (현재 쓸모없는) 관찰자가 여전히 통지를받을 때 메모리를 낭비하고 성능을 저하시킵니다.

약한 참조는 관찰자가 가비지 수집 될 수 있도록이 메모리 누수를 수정합니다. 피험자가 모든 관찰자에게 알리고 관찰자에 대한 약한 참조 중 하나가 비어 있음을 발견하면 해당 참조를 안전하게 제거 할 수 있습니다. 대안으로, 약한 참조는 피험자가 수집시 관찰자를 제거 할 정리 콜백을 등록 할 수있는 방식으로 구현 될 수 있습니다.

그러나 약한 참조는 관찰자를 제거하는 것을 잊어서 피해를 제한하는 반창고 일뿐입니다. 올바른 해결책은 더 이상 필요하지 않을 때 관찰자를 제거하는 것입니다. 옵션은 다음과 같습니다.

  • 수동으로 수행하지만 오류가 발생하기 쉽습니다.

  • Java 또는 usingC #에서 리소스를 사용하는 것과 비슷한 것을 사용합니다 .

  • RAII 관용구를 통한 결정 론적 파괴. 결정 론적 가비지 콜렉션이있는 언어에서는 소멸자를 트리거하기 위해 여전히 대상에서 관찰자에 대한 약한 참조가 필요할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.