iOS - RxSwift / Subject

이한솔·2024년 7월 1일
0

iOS 앱개발 🍏

목록 보기
50/53

Subject

Subject는 Observable과 Observer의 역할을 모두 수행할 수 있는 특별한 형태의 Observable로 데이터를 발행하고 구독할 수 있는 능력을 모두 가지고 있다.



Observabel과 Subject의 차이

데이터 방출 시점

Observable은 해당 Observable을 subscribe하면 그 시점에 값을 방출한다.

import RxSwift

let disposeBag = DisposeBag()

// Observable 생성
let observable = Observable<Int> { observer in
    observer.onNext(1)
    observer.onNext(2)
    observer.onNext(3)
    observer.onCompleted()
    return Disposables.create()
}

// 첫 번째 구독
observable.subscribe(onNext: { value in
    print("First subscription:", value)
}).disposed(by: disposeBag)

// 두 번째 구독
observable.subscribe(onNext: { value in
    print("Second subscription:", value)
}).disposed(by: disposeBag)


// 출력
First subscription: 1
First subscription: 2
First subscription: 3
Second subscription: 1
Second subscription: 2
Second subscription: 3

Subject는 Observable과 다르게 구독 여부와 상관 없이 이벤트를 방출하는 Observable으로 구독하는 시점에 따라 방출되는 결과가 다르다.
따라서, Observable처럼 어떤 항목을 방출할 것인지를 생성 당시 정의해두는 것이 아니라, 원하는 시점마다 항목을 방출할 수 있다.

import RxSwift

let disposeBag = DisposeBag()
let subject = PublishSubject<Int>()

// 첫 번째 구독
subject.subscribe(onNext: { value in
    print("First subscription:", value)
}).disposed(by: disposeBag)

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

// 두 번째 구독
subject.subscribe(onNext: { value in
    print("Second subscription:", value)
}).disposed(by: disposeBag)

subject.onNext(3)
subject.onCompleted()


// 출력
First subscription: 1
First subscription: 2
First subscription: 3
Second subscription: 3

데이터 방출 방식

Observable은 unicast로 각각의 Observer들이 개별적인 값을 받는다.

import RxSwift

let disposeBag = DisposeBag()

let observable = Observable<Int> { observer in
    observer.onNext(Int.random(in: 1...100))
    observer.onCompleted()
    return Disposables.create()
}

observable.subscribe(onNext: { value in
    print("First subscription received:", value)
}).disposed(by: disposeBag)

observable.subscribe(onNext: { value in
    print("Second subscription received:", value)
}).disposed(by: disposeBag)

// 출력
First subscription received: 42
Second subscription received: 99

Subject는 multicast로 모든 Observer들이 동일한 값을 받는다. 하나의 Observable의 실행이 여러 Subscribe에게 공유된다.

import RxSwift

let disposeBag = DisposeBag()
let subject = PublishSubject<Int>()

subject.subscribe(onNext: { value in
    print("First subscription received:", value)
}).disposed(by: disposeBag)

subject.subscribe(onNext: { value in
    print("Second subscription received:", value)
}).disposed(by: disposeBag)

subject.onNext(Int.random(in: 1...100))


// 출력
First subscription received: 42
Second subscription received: 42

외부에서 값 변경 가능 여부

Observable은 한 번 생성된 이후 외부에서 값을 변경할 수 없다. 즉, Observable은 정의된 값만을 방출하며, 외부에서 방출되는 값을 수정하거나 추가할 수 없다.
Subject는 외부에서 값을 변경하고, 이를 구독자들에게 방출할 수 있다. 이는 Subject가 Observer의 역할도 수행하기 때문에 가능한 것이다. 예를 들어, 다음과 같이 Subject에 값을 직접 추가할 수 있다.

import RxSwift

let subject = PublishSubject<Int>()

subject.subscribe(onNext: { value in
    print("Subject value: \(value)")
})

// 외부에서 Subject에 값을 추가
subject.onNext(100)
subject.onNext(200)

// 출력
Subject value: 100
Subject value: 200


Subject의 종류

PublishSubject

PublishSubject는 구독이 시작된 시점 이후에 발생한 이벤트만을 구독자에게 전달한다. 초기에는 어떠한 값도 가지지 않으며, 새로운 구독자는 구독 시점 이후의 이벤트만을 받는다.

import RxSwift

let disposeBag = DisposeBag()
let subject = PublishSubject<String>()

subject.onNext("Hello") // 구독자가 없으므로 전달되지 않음

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

subject.onNext("World") // "World" 방출
subject.onCompleted()   // 완료 이벤트 방출

BehaviorSubject

BehaviorSubject는 초기값을 가지며, 새로운 구독자에게 가장 최신의 값과 그 이후에 발생하는 모든 이벤트를 전달한다.

import RxSwift

let disposeBag = DisposeBag()
let subject = BehaviorSubject(value: "Initial Value")

subject.subscribe(onNext: { value in
    print("First subscriber:", value)
}).disposed(by: disposeBag)

subject.onNext("Hello")
subject.onNext("World")

subject.subscribe(onNext: { value in
    print("Second subscriber:", value)
}).disposed(by: disposeBag) // 가장 최근 값인 "World"부터 방출

ReplaySubject

ReplaySubject는 버퍼 크기를 지정할 수 있으며, 지정된 버퍼 크기만큼의 최신 이벤트를 저장하고, 새로운 구독자에게 해당 이벤트를 모두 전달한다.
BehaviorSubject와 달리 초기값을 설정해주지 않기 때문에, 만약 아무 항목도 방출하지 않았을 때는 해당 Subject를 구독해도 아무런 값도 방출하지 않는다. 버퍼의 크기는 필요한 만큼만 지정해주는 것이 메모리 관리상 중요하다.

import RxSwift

let disposeBag = DisposeBag()
let subject = ReplaySubject<String>.create(bufferSize: 2)

subject.onNext("One")
subject.onNext("Two")
subject.onNext("Three")

subject.subscribe(onNext: { value in
    print("First subscriber:", value)
}).disposed(by: disposeBag) // "Two"와 "Three" 방출

subject.onNext("Four")

subject.subscribe(onNext: { value in
    print("Second subscriber:", value)
}).disposed(by: disposeBag) // "Three"와 "Four" 방출

AsyncSubject

AsyncSubject는 Observable이 완료되기 전까지는 아무런 이벤트도 방출하지 않다가, 완료된 시점에 가장 최신의 값을 단 한 번 방출한다.

import RxSwift

let disposeBag = DisposeBag()
let subject = AsyncSubject<String>()

subject.subscribe(onNext: { value in
    print(value)
}, onCompleted: {
    print("Completed")
}).disposed(by: disposeBag)

subject.onNext("Hello")
subject.onNext("World")
subject.onCompleted() // "World" 방출 후 완료


참고자료

0개의 댓글