티스토리 뷰

RxSwift

[RxSwift] Observable, Subscribe

Hani_Levenshtein 2021. 6. 25. 09:15

 

안녕하세요 Hani입니다.

비동기 처리를 위한 RxSwift에 대한 공부를 시작해보겠습니다. 😎

 

먼저, 앞으로 볼 화면이 어떻게 구성되어있는지 보겠습니다.

버튼을 누르면 화면이 잠시 멈춘다.

 위 화면은 아래와 같이 동기적으로 작성되어 있습니다.

 

 

위 코드에 따르면 모든 작업이 Main Queue에서만 실행되는데,

JSON 정보를 가져오는 작업이 실행되고 완료되기까지 타이머를 비롯한 모든 UI 요소들이 멈추게 됩니다.

 

따라서 화면이 멈추는 것을 막기 위해 JSON 정보를 가져오는 작업을 Global Queue에서 비동기적으로 진행할 필요가 있습니다. 

비동기 처리와 스레드 분리를 위해 DispatchQueue를 이용하여 작업을 Main Queue와 Global Queue로 분리해보겠습니다.

 

 

JSON 정보를 가져오는 부분을 Global Queue로 옮겼습니다.

텍스트뷰와 인디케이터는 UI 요소기 때문에 Main Queue에서 동작하도록 만들었습니다. 

 

비동기적으로 작성된 코드의 동작을 살펴봅시다.

 

json을 받아오는 동안 타이머가 멈추지 않는다.

 

json을 가져오는 부분이 Global Queue에서 비동기적으로 작동하기 때문에 타이머 UI가 멈추게 만들지 않습니다.

인디케이터도 제대로 동작하는 것을 볼 수 있습니다.

 

 

 

여기서 코드를 더 간단히 만들기 위해 비동기적으로 실행되는 부분을 따로 함수로 만들어 보면?

안타깝게도 Global Queue에서 비동기로 얻어낸 결과를 return으로 전달할 수 없습니다. 🥺

 

 

따라서 비동기적으로 생성된 결과를 return으로 전달하지 않고 클로저로 전달해야 하는데..

클로저를 쓰는 게 싫다! 🔥

 

어떻게 하면 return으로 결과를 전달해줄 수 있을까!

비동기 처리로 얻은 결과를 어떤 클래스로 감싸면 이를 return으로 전달해줄 수 있습니다. 

 

이렇게 비동기적으로 생성되는 결과를 클로저가 아닌 리턴으로 전달할 수 있게 도와주는 라이브러리가 몇 개 있는데

그중 하나가 RxSwift..! 

 

 

위 코드는 RxSwift형식으로 코드를 바꾼 것입니다.

 

Observable = 나중에 생성된 데이터

subscribe = 데이터가 생기면

 

비동기 처리에 의해 나중에 생성된 데이터를 Observable로 감싸서 return으로 보내주고,

return의 결과물을 subscribe한 다음,

onNext(전달), onCompleted(전달 완료), onError(오류) 중 어느 이벤트인지에 따라 달리 처리하면 됩니다.

 

 

 

저기 노란색 경고 줄의 의미는

subscribe의 결과물이 disposable인데 disposable을 이후에 사용하지 않았기 때문..!

disposable는 어떻게 쓰는건데? 🧐

 

observable.subscribe을 보면 안에 있는 클로저가 self를 캡처하면서 레퍼런스 카운트가 증가합니다.

클로저가 메모리에 계속 남아있으면 순환 참조가 일어나겠죠?

그래서 순환 참조를 막기 위해 클로저를 끝내야 합니다.

 

클로저를 종료시키는 방법은 observable이 종료되는 것입니다.

observable이 종료되는 조건은 onCompleted, onError, Disposed 총 3가지입니다.

 

위에서도 사실 onNext로 데이터가 전달된 다음, 전부 전달되었음을 onCompleted로 알려야 클로저가 끝나고 순환 참조가 없어집니다.

 

 

 

아무튼 다시 disposable로 돌아가 보면

disposable에 대한 메서드가 보이는데 첫 번째 메서드를 실행해보겠습니다.

결과물이 안나와

비동기적으로 데이터를 처리하기도 전에 disposable을 dispose했기 때문에 결과물이 나오지 않습니다.

때문에 dispose는 명확히 observable을 종료시키고 싶은 타이밍에 사용하고

 

대신

disposable의 두 번째 메서드인 disposed를 통해 disposable를 disposeBag에 담습니다.

 

disposeBag은 맨 처음 화면을 구성하는 코드에 있던 녀석입니다.

여러 disposable을 disposeBag에 담아놓으면 화면 전환 등에 의해 disposeBag의 뷰 컨트롤러가 메모리에서 해제되면 멤버 변수인 disposeBag도 같이 끝나고 disposeBag이 가지고 있는 disposable도 결국 끝납니다.

 

결국 dispose/disposed는

1. observable을 어느 명확한 시점에서 종료하고 싶을 때

2. 순환 참조를 막고 싶을 때

필요한데

 

2번의 경우엔 변수에 굳이 담을 필요 없이 disposed를 통해 disposeBag에 담고, subscribe의 반환 값은 언더바 처리해주면 됩니다.

 

 

순환 참조를 막기 위해 Observable의 생명주기를 다시 살펴보면..!

큰 관점에서 Observable.create().subscribe{}.disposed() 처럼 진행됩니다.

 

여기서 subscribe에서 onError나 onCompleted를 만나면 클로저가 종료되긴 하지만

 

Using dispose bags or takeUntil operator is a robust way of making sure resources are cleaned up. We recommend using them in production even if the sequences will terminate in finite time.

 

메모리 누수를 안전하게 막기 위해 권장된 방법(disposeBag / takeUntil)을 사용하는 것이 좋겠습니다.

(takeUntil은 다음에 알아볼..!)

 

 

 

이번 포스팅에서는

1. 비동기 처리로 생성된 데이터의 Observable을 통한 return

2. Observable의 subscribe을 통한 데이터 처리

3. RxSwift의 Memory Leak 방지법

을 알아봤습니다. 🥰


References

https://www.youtube.com/watch?v=iHKBNYMWd5I

https://github.com/iamchiwon/RxSwift_In_4_Hours

https://github.com/ReactiveX/RxSwift/blob/main/Documentation/GettingStarted.md#disposing

https://medium.com/gett-engineering/disposing-rxswifts-memory-leaks-6ceb73162170

 

댓글