본문 바로가기

Swift/RxSwift

[🔥Rx뿌셔] Subscribe

728x90

Observable에서 이벤트를 방출하면 이를 구독하고 있는 Observer가 해당 이벤트에 대한 처리를 한다.

이때 구독을 어떻게 하는가? .. 바로 subscribe 메서드를 통해서 할 수 있다.

 

🔔 구독

좋아요 알림 설정까지 ~ 가 아니고 ..

구독을 의미하는 메서드가 위에서 말한 것과 같이 바로 subscribe 이다. 

 

어떤 Observable을 subscribe하려고 하면 아래와 같이 Observable이 방출하는 이벤트에 대해서 처리할 수 있도록 함수가 나온다.

그리고 해당 자동 완성된 메서드를 받아오면,

 

위와 같이 Observable에서 방출한 아이템에 대한 처리를 할 수 있다.

 

만약 제대로 이벤트가 방출되어 온다면, 첫번째 코드블럭으로 이벤트가 들어가게 되고 이 코드 블럭이 실행된다.

그러나 메서드 이름에서 알 수 있는 것처럼 중간에 오류가 있어서 제대로 처리가 되지 않으면 onError 코드 블럭이 실행된다.

 

이벤트에 대해 모두 처리를 했다면, (완료 되었다면) onCompleted 코드 블럭이 실행되고,

complete가 실행되고 나서 dispose가 이뤄지므로 onCompleted 코드 블럭 이후로 onDisposed 코드 블럭이 실행된다.

 

 

필요한 이벤트만 골라서 사용하고 싶다면?

만약 UI와 관련된 이벤트이기 때문에 onError에 대한 처리를 할 필요가 없다면? 

위와 같이 필요하지 않은 부분의 코드 블럭(클로저)을 삭제하면 된다.

 

🔥 근데 이렇게 모든 이벤트에 대한 처리를 명시해주지 않고 생략했을 때 주의할 점이 있다.

위와 같이 여러 이벤트 타입이 생략된 경우에는 이벤트들을 따로 구분하지 않게 된다.

 

다시 말해서 아래 이미지의 주석 부분 코드에서 onNext, onError가 생략되어 있으므로 추론이 안된다는 것이다.

즉, onNext / onError를 동시에 구분하지 않고 처리한다는 것이다.

만약 onNext만 생략이 되어 있다면 충분히 추론할 수 있지만, 여러 이벤트가 동시에 생략이 되어 있다면 추론이 어렵다.

 

그리고 위의 코드에서 onCompleted까지 생략을 한다면 onCompleted도 같은 코드 블럭 안에서 처리하게 된다. (하나의 코드 블럭 안에서)

 

그렇다면, 원하지 않을 때에도 하늘색 내부에 짜둔 코드가 onError / onCompleted 이벤트에 의해 실행 될 수 있다.

 

그래서 !! 만약 생략을 해서 subscribe를 하게 된다면 !!

onNext나 이벤트 타입을 꼭 명시하는 것이 안전하다 !!

 

 


🚰 줄줄 샌다 메모리가 줄줄

내 눈에도 눙물이 줄줄 나겠지 .. 

 

위에서 Observable에 대한 이벤트를 Observer가 subscribe해서 이벤트에 대한 처리를 하는 코드를 알아보았다.

코드에서 알 수 있는 것처럼 각 이벤트에 대한 처리를 클로저 내부에서 할 수 있다.

 

네?

네!

 

각 이벤트에 대한 처리를 클로저에서 하고 있다.

그렇다면 주의해야 하는 점은? 바로 메모리누수이다.

-> 메모리 누수를 방지하기 위해서 그동안 해주었던 약한 참조를 꼭 해주어야 한다.

 

물론 클로저 내부에서 self를 참조하는 경우에만 해당

private func bindData() {
    items
        .subscribe(onNext: { [weak self] itemList in
            guard let self = self else { return }
            self.doSomething(itemList)
        })
        .disposed(by: disposeBag)
}

private func doSomething(_ itemList: [String]){
    print(itemList.joined(separator: "\n"))
}

 

그런데, 이런 코드(이벤트 처리 클로저 안에 [weak self]로 약한 참조를 하는 코드)가 한두개가 아닐 것이다.

그럴 때마다 weak self로 처리를 하는 것은 조금 .. 번거로운 일이다 .. !

 

그래서 Rx에서도 매번 [weak self]를 호출하는 것을 조금 덜 번거롭게 할 수 있도록 이니셜라이저를 따로 만들어주었다.

#with: 

아래 이미지에서 볼 수 있는 것처럼 with:가 포함되어 있는 .subscribe()를 사용하면 된다.

 

with를 사용하게 되면 코드가 아래와 같이 바뀐다.

items
    .subscribe(with: self, onNext: { vc, itemList in
        vc.doSomething(itemList)
    })
    .disposed(by: disposeBag)

이런 식으로 with에 self를 넘겨주고 vc로서 다시 받아준 다음, vc를 클로저 내부에서 자유롭게 사용하면 된다.

(vc는 임의로 설정한 변수명이다.)

 

 

#withUnretained

그냥 메서드로 하나 호출하면 안되나?! 

네. 있습니다. 있어요.

 

items
    .withUnretained(self)
    .subscribe(onNext: { (vc, itemList) in
        vc.doSomething(itemList)
    })
    .disposed(by: disposeBag)

withUnretained(self) 코드를 통해서 해결할 수 있다.

 

 

각 경우의 코드를 한눈에 살펴보면 아래와 같다.

 private func bindData() {
     items
         .subscribe(onNext: { [weak self] itemList in
             guard let self = self else { return }
             self.doSomething(itemList)
         })
         .disposed(by: disposeBag)
     
     items
         .subscribe(with: self, onNext: { vc, itemList in
             vc.doSomething(itemList)
         })
         .disposed(by: disposeBag)
     
     items
         .withUnretained(self)
         .subscribe(onNext: { (vc, itemList) in
             vc.doSomething(itemList)
         })
         .disposed(by: disposeBag)
 }

매번 클로저 구문마다 [weak self]를 하는 것이 아니라, 아래 두 방법으로 하는 것이 코드가 훨씬 더 깔끔해진 것을 볼 수 있다.

 

 

🔥 정리
subscribe를 할 때 필요한 이벤트만 골라서 처리할 수 있다.
subscribe를 하고 나서 각 이벤트 처리는 클로저에서 진행하는데, 이 때 메모리 누수가 발생할 수 있으므로 주의해서 코드를 작성해야 한다. 

'Swift > RxSwift' 카테고리의 다른 글

[🌱SeSAC] Disposable, Observable, Subject  (0) 2022.10.26
[🔥Rx뿌셔] Disposable  (0) 2022.10.25
[🔥Rx뿌셔] Observable  (2) 2022.10.24
[🔥Rx뿌셔] Intro  (1) 2022.10.24
[🌱SeSAC] Creating and Subscribing to Observables  (0) 2022.10.24