Danny의 iOS 컨닝페이퍼
article thumbnail

Observable & Subscribe

사전적인 의미로 Observable은 "관찰 가능한"이라는 의미를 갖고 있습니다.

 

Observable은 이벤트를 방출하는 동시에

구독자(observer)들이 그 이벤트를 관찰할 수 있기 때문에 이와 같은 이름으로 지어졌습니다.

 

관찰 가능한 이벤트(Observable)를 구독(subscribe)하게 되면

이벤트(onNext, onError, onCompleted, onDisposed)를 통해 시간의 흐름에 맞춰 이벤트를 방출합니다.


NETFLIX로 간단히 비유를 통해 알아봅시다.

 

Observable ⌛️

NETFLIX의 영화나 드라마

(영상이 주기적으로 업데이트가 됩니다)

 

👀 Observer 👀

NETFLIX를 시청하는 사람

 

📦 subscribe 📦

NETFLIX 구독권

(구독권만 갖고 있다면 NETFLIX의 영화나 드라마가 업데이트가 되면 업데이트된 영상을 시청할 수 있다.)

 

구독자가 NETFLIX를 구독하고 있고

만약 NETFLIX에서 영상을 업데이트한다면 구독자도 업데이트된 최신 영상을 시청할 수 있겠죠?

 

즉, 구독자(Obeserver)가 Observable을 구독(subscribe)을 하고 있다면

Observable이 업데이트가 되더라도 즉시 업데이트된 정보를 갖고 이벤트를 처리할 수 있습니다.

 

Observable은 일회성을 갖고 있기 때문에 비유가 적절하진 않지만

대략적으로 Observable과 subscribe는 이러한 관계를 갖고 있습니다.


 

이벤트 타입의 종류

Obeserver는 프로토콜 ObeserverTpye을 준수하는 객체이다.

(ObeserverTpye에는 Obeservable이 방출하는 데이터의 이벤트를 처리하는 메서드가 정의되있다.)

 

Observable이 방출하는 이벤트는 크게 네 가지로 존재합니다.

 

onNext, onError, onCompleted, onDisposed

 

onNext

Observable에서 새로운 Element를 방출할 때 onNext 메서드가 호출됩니다. (여러 번 호출 가능)

-> NETFLIX에 새로운 영상이 업데이트된 상황

 

onError

Observable에서 새로운 오류가 발생할 때 onError 메서드가 호출됩니다.

-> 어떠한 오류로 인해 NETFLIX 서비스를 이용하지 못하는 상황

 

onCompleted

Observable에서 모든 onNext 작업을 마치거나 onError가 발생하면 onCompleted 메서드가 호출됩니다.

-> NETFLIX의 모든 영상 업데이트되거나 오류가 발생한 상황

 

onDisposed

Observable이 모두 종료되고 폐기(dispose)됐을 때 onDisposed 메서드가 호출됩니다.

-> NETFLIX가 서비스가 종료된 상황

 

 

사용 방법

오늘은 사용 방법 및 원리를 간단하게 알아보고

추후에 Observable의 여러 생성 방법에 대해 자세히 올리겠습니다.

 

Observable의 이벤트를 처리하기 위해선 구독(subscribe)이 반드시 필요합니다.

서로 떼려야 땔 수 없는 관계죠.

 

아래와 같이 사용합니다.

// Observable 생성
let observable = Observable.of(1, 2, 3)

// 이와 같이 만들면 구독은 하지만 Observable이 갖고있는 이벤트 처리를 하지 않는다.
observable.subscribe()

 

현재 구독(subscribe)은 하고 있지만 Observable이 갖고 있는 이벤트 처리를 하지 않고 있습니다.

구독(subscribe)에서는 반드시 받은 이벤트(onNext, onError, onCompleted, onDisposed)를 처리해줘야 합니다.

 

두 가지 방법으로 처리가 가능하죠.

 

 

방법 1

Observable 이벤트 자체를 받아와 switch문으로 처리하는 방법

observable.subscribe(on: (Event<T>) -> Void)
observable
    .subscribe { event in
        switch event {
        case .next(let element):
            print("Observable로 부터 \(element)를 전달 받았습니다.")
        case .completed:
            print("Observable이 정상적으로 종료 되었습니다.")
        case .error(let error):
            print("Observable이 에러가 발생했습니다: \(error)")
        }
    }

// Observable로 부터 1를 전달 받았습니다.
// Observable로 부터 2를 전달 받았습니다.
// Observable로 부터 3를 전달 받았습니다.
// Observable이 정상적으로 종료 되었습니다.

 

 

방법 2

클로저를 통해 각 이벤트 시점에 따라 이벤트를 처리하는 방법

observable.subscribe(onNext: ((T) -> Void)?,
                     onError: ((Error) -> Void)?,
                     onCompleted: (() -> Void)?, 
                     onDisposed: (() -> Void)?)
observable
    .subscribe { element in
        print("Observable로 부터 \(element)를 전달 받았습니다.")
    } onError: { error in
        print("Observable이 에러가 발생했습니다: \(error)")
    } onCompleted: {
        print("Observable이 정상적으로 종료 되었습니다.")
    } onDisposed: {
        print("Observable이 폐기됬습니다.")
    }

// Observable로 부터 1를 전달 받았습니다.
// Observable로 부터 2를 전달 받았습니다.
// Observable로 부터 3를 전달 받았습니다.
// Observable이 정상적으로 종료 되었습니다.
// Observable이 폐기됬습니다.

 

이 방법은 각각의 이벤트들이 옵셔널 정의돼 있어

필요한 이벤트만 선택하여 상황에 맞춰 사용할 수 있습니다.

// onNext 이벤트만 사용하고 싶을 때
observable
    .subscribe(onNext: { element in
        print("Observable로 부터 \(element)를 전달 받았습니다.")
    })

// Observable로 부터 1를 전달 받았습니다.
// Observable로 부터 2를 전달 받았습니다.
// Observable로 부터 3를 전달 받았습니다.

 

메모리 누수 해결 방법

구독(subscribe)은 escape closure로 동작하므로

만약 자신의 객체를 참조하는 self를 사용하게 되면 메모리 누수가 발생할 확률이 있습니다.

 

이를 방지하기 위해서 아래와 같이 [weak self]를 사용해줘야 하죠.

class Action {
    func someAction() {}
    
    init() {
        observable
            .subscribe { [weak self] element in
                guard let self = self else { return }
                self.someAction()
                
                print("Observable로 부터 \(element)를 전달 받았습니다.")
            }
    }
}

 

RxSwift에서는 메모리 누수를 간편히 해결하기 위해 따로 이니셜라이저를 만들어놨습니다.

// 파라미터 with가 있는 메서드
subscribe(with: Object)

 

with를 통해 객체 self를 넘겨주고 메서드 내부에서 [weak self]를 처리하여 클로저로 전달합니다.

클로저로 전달받은 self를 사용하여 메모리 누수를 방지해 줍니다.

class ActionRx {
    func someAction() {}
    
    init() {
    // 이 코드에선 self를 전달하여 내부에서 [weak self]을 처리하고 safeSelf로 전달
    // 안전한 safeSelf를 self 대신 사용가능
        observable
            .subscribe(with: self) { safeSelf, element in
                safeSelf.someAction()
                
                print("Observable로 부터 \(element)를 전달 받았습니다.")
            }
    }
}

 

with를 사용하는 방법이 코드의 길이도 짧고 직관적으로 보이네요.

둘 중 편리한 방법으로 사용하시면 됩니다. 

 

 

오늘은 Observable에 대한 간단한 개념 및 사용방법을 알아봤습니다.
다음에는 Obsevable 연산자에 알아보겠습니다.

링크를 참조해주세요.
[iOS/RxSwift] Observable 연산자

부족한 설명이지만, 조금은 이해 가셨나요?

틀린 내용이 있다면 언제든지 지적해 주시면 감사히 받겠습니다. 🫠
읽어주셔서 감사합니다 😃

 

 

반응형

'RxSwift' 카테고리의 다른 글

[iOS/RxSwift] Subject의 종류  (0) 2023.03.09
[iOS/RxSwift] Hot & Cold Observable  (0) 2023.03.09
[iOS/RxSwift] Traits에 대하여  (0) 2023.03.06
[iOS/RxSwift] Observable 연산자  (0) 2023.03.06
profile

Danny의 iOS 컨닝페이퍼

@Danny's iOS

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!