[RxSwift] Subjects

나는 사과·2021년 4월 27일
0

RxSwift

목록 보기
5/8

시작하기 앞서서 참고하고있는 깃허브에서 Subject를 Observable이자 Obserber인 것이라고 표현하고 있다. 처음에는 이 말의 뜻이 이해가 가지 않아서 찾아보니깐 Subject가 ObserverType 프로토콜을 채택하고 있고 Observable을 상속하고 있어서 그렇다고 한다.

public final class PublishSubject<Element>
    : Observable<Element>
    , SubjectType
    , Cancelable
    , ObserverType
    , SynchronizeUnsubscribeType {
...

Subject의 종류

Subject는 (Observer(Observable 구독) + Observable(이벤트 발생))과 같이 행동한다. 또한 .next이벤트를 받고 받을 때 마다 Subscribe에 방출한다.

다음은 4가지 타입의 Subject다.

  • PublishSubject : 빈 상태로 시작해서 새로운 값만 subscribe에 방출
  • BehaviorSubject : 하나의 초기값을 가진 상태로 시작해서 새로운 subscribe에게 초기값이나 최신값을 방출
  • ReplaySubject : 버퍼를 두고 초기화하고, 버퍼 사이즈 만큼의 값들을 유지하면서 새로운 subscribe에 방출
  • Variable : BehaviorSubject를 래핑하고 현재의 값을 상태로 보존한 다음 가장 최신이거나 초기 값만을 새로운 subscribe에 방출

PublishSubjects 사용

PublishSubject는 구독되는 순간 새로운 이벤트가 수신되었음을 알리고 싶을 때 사용하는 것이 용이하다. 이것은 구독을 종료하거나 Subject가 .error.completed이벤트를 통해서 완전 종료될 때까지 유효하다.

let subject = PublishSubject<Int>()
// subject에 .next 이벤트 방출
subject.onNext(1)

// subject 구독
let subscribeOne = subject.subscribe(onNext: {(value) in
    print(value)
})

// .next 이벤트 방출
subject.onNext(2)

// 구독취소
subscribeOne.dispose()

// 2

위 코드에서 처음 onNext(1)로 이벤트를 방출했음에도 구독할 때 출력이 되지 않았다. 그 이유는 PublishSubjects는 구독된 이후부터 종료되기 전까지 새로운 이벤트가 수신될 때마다 이벤트를 방출하기 때문입니다.

PublishSubjects는 시간에 민감한 데이터를 모델링하는 경우에 사용할 수 있다. (예를 들어, 실시간으로 이루어지는 어플리케이션)

BehaviorSubjects 사용

BehaviorSubjects는 마지막 .next이벤트를 새로운 구독자에게 반복한다는 점만 제외하고 PublishSubject와 유사하다.

let subject = BehaviorSubject(value: 1)
let disposebag = DisposeBag()

subject.subscribe{ (value) in
    print(value)
}.disposed(by: disposebag)

subject.onNext(2)

// next(1)
// next(2)

위 코드처럼 BehaviorSubjects는 생성할 때 발생한 1이 구독했을 때에도 이벤트가 반복되어서 방출됐다. 그래서 출력에서 next(1)이 출력되고 그 다음 next(2)가 이어서 출력이 되었다. 만약 PublishSubject였다면 next(2)만 출력이 됐을 것이다.

BehaviorSubjects는 뷰를 가장 최신의 데이터로 미리 채우기할 경우에 용이하다. 앱이 새로운 데이터를 로드하는 동안 최신의 값으로 뷰를 미리 나타낼 수 있기 때문이다.

ReplaySubject 사용

ReplaySubject는 생성시 지정한 크기까지 방출하는 요소를 일시적으로 캐시하거나 버퍼한 다음 해당 버퍼를 새로운 구독자에게 방출한다.
쉽게 말하자면.. subject의 버퍼의 크기는 2이고 이미 2개의 이벤트를 방출한 다음 구독을 하게 되면 버퍼의 크기인 2만큼 이전의 이벤트를 받은 다음 다음 이벤트를 받게 된다.
그만큼 버퍼는 메모리를 차지하기 때문에 이미지나 array같은 메모리를 많이 차지하는 값들을 큰 사이즈의 버퍼로 갖는건 메모리에 많은 부하를 준다고 한다.

let subject = ReplaySubject<Int>.create(bufferSize: 2)
let disposebag = DisposeBag()

subject.onNext(1)
subject.onNext(2)

subject.subscribe{ (value) in
    print("1) \(value)")
}.disposed(by: disposebag)

subject.onNext(3)

subject.subscribe{ (value) in
    print("2) \(value)")
}.disposed(by: disposebag)

// 1) next(1)
// 1) next(2)
// 1) next(3)
// 2) next(2)
// 2) next(3)

위 코드를 보면 처음 버퍼의 크기가 2인 ReplaySubject를 생성하고 2개의 이벤트를 방출한 다음 구독을 하면 2개의 이벤트(1,2)가 출력이 된다. 그 다음 이벤트(3)을 방출하면 첫 번째 구독자에서 출력이 이루어진다. 그리고 두 번째 구독을 하게 되면 크기가 2인 버퍼에 들어있는 이벤트(2,3)이 출력이 된다.

ReplaySubject는 최근 값외에 더 많은 것을 보여주는 최근 검색어같은 사용처에 사용할 수가 있다.

Variables 사용

Variables는 BehaviorSubject를 래핑하고 현재 값을 상태로 갖고 있다. 다른 것들과 다른 점은 일단 onNext()를 사용할 수 없고 에러가 발생하지 않는다는 것을 보증하기 때문에 .error 이벤트도 방출할 수 없다. 그리고 할당 해지되면 자동으로 완료가 되기 때문에 수동으로 .completed를 할 필요가 없다.

let variable = Variable("Initial value")
let disposebag = DisposeBag()

variable.value = "New value"

variable.asObservable().subscribe{ (value) in
    print("1) \(value)")
}.disposed(by: disposebag)

variable.value = "Set other value"

variable.asObservable().subscribe{ (value) in
    print("2) \(value)")
}.disposed(by: disposebag)

variable.value = "Set other value2"

// 1) next(New value)
// 1) next(Set other value)
// 2) next(Set other value)
// 1) next(Set other value2)
// 2) next(Set other value2)

Variable는 항상 최신 값을 갖고 있어서 값이 갱신이 되면 출력을 다시 하면서 최신 값을 갖고있도록 한다.
Varaible는 유동적이고 현재 어떤 값을 갖고있는지 확인하고 싶을 때 사용하면 유용하다.

0개의 댓글