⚙️ Programming/C# & Unity

[Unity] UniRX - 4 IObservable의 Dispose와 스트림의 생명 주기

Sugar0810 2023. 1. 16. 16:10

Dispose

public interface IObservable<T>
{
    IDisposable Subscribe(IObserver<T> observer);
}
  • Subscribe의 반환값인 IDisposable 은 가비지컬렉터가 관리하지 않는 리소스를 해제해 주기 위한 C#의 기본 인터페이스
  • IDisposable.Dispose()로 해제
var subject = new Subject<int>();

// IDispose를 저장
var disposable = subject.Subscribe(x => Debug.Log(x), () => Debug.Log("OnCompleted"));  //Subscribe(OnNext, OnCompleted) 오버로드

subject.OnNext(1);
subject.OnNext(2);
// Subscribe 종료
disposable.Dispose();
// UnSubscribe
subject.OnNext(3);
subject.OnCompleted();

// Result
1
2
  • OnCompleted 이벤트가 아니라 Dispose() 로 구독을 종료했을 경우 OnCompleted를 전달하지 않는다는 점을 주의해야 한다.
  • OnCompleted는 호출하는 Subject의 모든 스트림의 Subscribe가 중단되지만 Dispose는 Subject의 스트림 중 원하는 한 개의 스트림만 구독을 중단할 수 있다.
var subject = new Subject<int>();

// IDispose를 저장
var disposable1 = subject.Subscribe(x => Debug.Log("스트림1:" + x), () => Debug.Log("OnCompleted"));
var disposable2 = subject.Subscribe(x => Debug.Log("스트림2:" + x), () => Debug.Log("OnCompleted"));
subject.OnNext(1);
subject.OnNext(2);

// 스트림1 만 Subscribe 종료
disposable1.Dispose();

subject.OnNext(3);
subject.OnCompleted();

//---결과---//
스트림1:1
스트림2:1
스트림1:2
스트림2:2
스트림2:3  // 스트림1:3 은 Dispose 로 인해 더이상 Subscribe 되지 않는다.
2.OnCompleted

 

Stream의 Life Cycle

스트림은 편리하게 사용할 수 있지만 객체의 잦은 생성과 삭제가 퍼포먼스에 큰 영향을 미치는 유니티 특성상 특히 수명 관리에 신경을 써야 한다.

 

기본적으로 스트림은 Subject에 포함 되는 일부 또는 그 자체라고 볼 수 있으며 Subject가 Destroy 되면 스트림도 파기된다.

 

하나하나의 스트림은 Subscribe 가 끝단에 위치하여 흘러온 데이터를 처리하게 되며 이 각각의 스트림 들은 Subject에 등록되어 있다.

 

즉, Subject 가 파기되지 않고 남아있다면 각각의 스트림들도 살아있다는 뜻이 된다.

 

때문에 스트림이 참조하고 있는 오브젝트가 스트림보다 먼저 Destroy 되어 버리면 해당 스트림의 처리는 정상적으로 이루어질 수 없음에도 여전히 살아남아 NullReferenceException을 일으키거나 메모리릭 등 성능 저하와 치명적인 오류를 발생하는 원인이 될 수 있다.

 

하지만 사용될 오브젝트가 런타임에서 결정되거나 파기 타이밍을 매번 코딩하는 단계에서 확인하기 힘든 경우가 많기 때문에 스트림에서 일일이 오브젝트를 검사하는 것도 쉽지 않은 일이다.

 

이런 경우 가장 손쉽게 처리할 수 있는 방법은 AddTo 오퍼레이터를 사용하는 것이다.

var subject = new Subject<int>()
	
subject
	.Where(x => x > 5)
	.Subscribe(x =>
	{
	// ---처리할 내용들...
	// ------------------
	}).AddTo(gameObject); // 지정한 GameObject 가 파기되면 이 스트림을 Dispose 한다.

AddTo 오퍼레이터에 GameObject의 인스턴스를 지정하면 해당 인스턴스가 Detroy 되는지를 감시하여 자동으로 Dispose를 호출하여 구독을 중단시켜 준다.

 


 

📚 참고사이트