안녕하세요🐶
빈지식 채우기의 비니🙋🏻♂️ 입니다.
이번 포스팅에서는 RxSwift에서 자주 사용하는 개념인 Subject에 대해 알아보도록 하겠습니다!!
1. Subject
Observer 이자 Observable 입니다.
Observer 이기 때문에 하나 이상의 Observable을 구독할 수 있으며,
동시에 Observable 이기 때문에 새로운 항목들을 방출할 수 있습니다.
여기서 나오는 Observer과 Observable에 대해 짧게 정리하고 가겠습니다.
Observable란,
비동기 이벤트를 관찰이 가능한 형태로 만든 것으로,
해당 이베트가 발생했을 시 항목을 방출합니다.
Observer란,
내가 원하는 비동기 이벤트를 방출하는 Observable이 있을 경우,
해당 Observable을 구독하여 항목이 방출되었을 때 그 항목을 받아 처리할 수 있습니다.
고로 Subject란,
Observable 과 Observer의 역할을 모두 수행할 수 있다! 라고 생각하시면 됩니다.
예시를 통해 확인해도록 하겠습니다!
let observable = Observable<Int>.create { (ob) -> Disposable in
ob.onNext(1)
ob.onNext(2)
ob.onNext(Int.random(in: 0..<100))
ob.onCompleted()
ob.onNext(3)
return Disposables.create()
}
let subscribe1 = observable.subscribe {
print("첫 번째 구독 : \($0)")
}
let subscribe2 = observable.subscribe {
print("두 번째 구독 : \($0)")
}
Observable은 다음과 같이
onNext, onError, onCompleted 등을 방출하는 것으로,
subscribe를 통해 Observable을 구독하여 방출하는 데이터가 어떤 것인지 관찰하는 기능을 수행합니다.
Observable의 경우 "독자적인 실행" 이므로,
Int.random 의 경우 각각의 구독에서 다른 값들을 방출한다.
다만! Subjectd의 경우.
let subject = PublishSubject<Int>()
subject.subscribe(
onNext: {
print("첫 번째 구독 : \($0)")
},
onCompleted: {
print("onCompleted")
}
)
subject.onNext(1)
subject.onNext(2)
subject.onNext(3)
subject.subscribe(
onNext: {
print("두 번째 구독 : \($0)")
},
onCompleted: {
print("onCompleted")
}
)
subject.onNext(4)
subject.onNext(Int.random(in: 0..<100))
subject.onCompleted()
Observable과 Observer의 역할을 동시에 수행하는 것이고,
구독(Subscribe) 하는 시점 이후에 발행되는 이벤트만 받는 차이점이 존재합니다.
또한 자신을 구독하는 여러 Observer들 모두에게 그 값을 전달하는 Observer의 역할도 합니다.
두 번째 구독 이이전에 onNext(1), onNext(2)의 경우 구독 시점 이전의 일이기 때문에 발행된 항목을 받을 수 없습니다.
Observable과 다르게 Subject의 경우,
여러 구독(Subscribe) 끼리 "공유" 를 해서 Int.random 에서 동일한 값들이 방출된다.
2. Subject의 종류
2-1. PublishSubject
PublishSubject는 구독 이후에 방출된 항목들만 Observer에게 전달합니다.
위 다이어그램에서 두 번째 구독의 경우 이전에 방출되었던 빨간색과 녹색은 받지 못하고 있습니다.
코드 예시로 확인해보시죠!
let subject = PublishSubject<String>()
subject.subscribe(onNext: {
print("첫 번째 구독 : \($0)")
}).disposed(by: disposeBag)
subject.onNext("1")
subject.onNext("2")
subject.subscribe(onNext: {
print("두 번째 구독 : \($0)")
}).disposed(by: disposeBag)
subject.onNext("3")
구독한 이후 시점에 방출되는 이벤트만 Observer가 받을 수 있습니다.
2-2. BehaviorSubject
BehaviorSubject는 구독할 경우,
Observable이 가장 최근에 발행한 항목( 없으면 초기 설정한 값 ) 을 한번 방출합니다.
1. 첫 번째 구독에서 발행한 항목이 없어서 초기 설정한 핑크색 항목을 방출합니다.
2. 두 번째 구독의 경우 첫 번째 구독의 가장 최근 발행한 항목을 바로 방출합니다.
코드 예시로 확인을 해보겠습니다.
let subject2 = BehaviorSubject<Int>(value: 1) // 초기값 설정
subject2.subscribe(onNext: {
print("첫 번째 구독 : \($0)")
}).disposed(by: disposeBag)
subject2.onNext(2)
subject2.onNext(3) // 최근 방출된 항목
subject2.subscribe(onNext: {
print("두 번째 구독 : \($0)")
}).disposed(by: disposeBag)
2-3. ReplaySubject
ReplaySubject란,
최신 이벤트를 버퍼 사이즈에 맞게 저장하고 구독 시 버퍼사이즈에 맞게 항목을 방출합니다.
1. 해당 그림을 통해 ReplaySubject 는 버퍼 사이즈 2로 설정하였습니다.
2. 첫 번째 구독에서 빨간색과 초록색 항목을 방출하였습니다.
3. 이후 두 번째 구독에서 버퍼 사이즈로 설정한 값(2)에 따라 빨간색과 초록색을 바로 방출합니다.
코드 예시로 확인해보겠습니다.
let subject34 = ReplaySubject<Int>.create(bufferSize: 3) // 버퍼 3 설정
subject34.subscribe(onNext: {
print("첫 번째 구독 : \($0)")
}).disposed(by: disposeBag)
subject34.onNext(2)
subject34.onNext(3) // 버퍼 3 설정을 통해 두 번째 구독에서 방출
subject34.onNext(4) // 버퍼 3 설정을 통해 두 번째 구독에서 방출
subject34.onNext(5) // 버퍼 3 설정을 통해 두 번째 구독에서 방출
subject34.subscribe(onNext: {
print("두 번째 구독 : \($0)")
}).disposed(by: disposeBag)
버퍼 사이즈를 3 설정하였기 때문에 두 번째 구독에서 3, 4, 5 3개의 항목을 방출합니다.
2-4. AsyncSubject
AsyncSubject 이란,
Completed 이벤트가 전달되기 전까지 어떠한 항목도 방출하지 않습니다.
Completed 이벤트가 전달되면, 가장 최근에 전달된 값으로 방출합니다.
1. 빨간색, 초록색, 파랑색이 전달 되었습니다.
2. Completed 이벤트 호출 시, 가장 최근에 전달된 파란색이 방출됩니다.
코드 예시로 확인해보겠습니다.
let subject4 = AsyncSubject<Int>()
subject4.subscribe(onNext: {
print("첫 번째 구독 : \($0)")
}).disposed(by: disposeBag)
subject4.onNext(1)
subject4.onNext(2)
subject4.onNext(3)
subject4.onNext(4) // 가장 최근에 전달된 값이 방출된다.
subject4.onCompleted() // onCompleted 호출 시 항목 방출
3. 정리
PublishSubject | BehaviorSubject | ReplaySubject | AsyncSubect | |
구독 시 방출되는 최근 값 | X | 가장 최근 값 ( 없을 시 초기설정 값 ) |
버퍼 크기만큼의 최근 값 | X |
특징 | 구독 이후에 방출된 이벤트 | 가장 최근 값 | 버퍼만큼의 가장 최근 값 | onCompleted 호출 시 가장 최근 값 |
이상으로 Subject 포스팅을 마치겠습니다.
전체 소스는 아래와 같이 첨부하도록 하겠습니다.
import Foundation
import RxSwift
import RxCocoa
class Subject {
func aboutSubect() {
print("----------------------------------------------------------")
let observable = Observable<Int>.create { (ob) -> Disposable in
ob.onNext(1)
ob.onNext(2)
ob.onNext(Int.random(in: 0..<100))
ob.onCompleted()
ob.onNext(3)
return Disposables.create()
}
let subscribe1 = observable.subscribe {
print("첫 번째 구독 : \($0)")
}
let subscribe2 = observable.subscribe {
print("두 번째 구독 : \($0)")
}
print("----------------------------------------------------------")
let subject = PublishSubject<Int>()
subject.subscribe(
onNext: {
print("첫 번째 구독 : \($0)")
},
onCompleted: {
print("onCompleted")
}
)
subject.onNext(1)
subject.onNext(2)
subject.onNext(3)
subject.subscribe(
onNext: {
print("두 번째 구독 : \($0)")
},
onCompleted: {
print("onCompleted")
}
)
subject.onNext(4)
subject.onNext(Int.random(in: 0..<100))
subject.onCompleted()
print("----------------------------------------------------------")
}
func publishSubject() {
let disposeBag = DisposeBag()
print("----------------------------------------------------------")
let subject = PublishSubject<String>()
subject.subscribe(onNext: {
print("첫 번째 구독 : \($0)")
}).disposed(by: disposeBag)
subject.onNext("1")
subject.onNext("2")
subject.subscribe(onNext: {
print("두 번째 구독 : \($0)")
}).disposed(by: disposeBag)
subject.onNext("3")
print("----------------------------------------------------------")
let subject2 = BehaviorSubject<Int>(value: 1)
subject2.subscribe(onNext: {
print("첫 번째 구독 : \($0)")
}).disposed(by: disposeBag)
subject2.onNext(2)
subject2.onNext(3)
subject2.subscribe(onNext: {
print("두 번째 구독 : \($0)")
}).disposed(by: disposeBag)
print("----------------------------------------------------------")
let subject34 = ReplaySubject<Int>.create(bufferSize: 3)
subject34.subscribe(onNext: {
print("첫 번째 구독 : \($0)")
}).disposed(by: disposeBag)
subject34.onNext(2)
subject34.onNext(3)
subject34.onNext(4)
subject34.onNext(5)
subject34.subscribe(onNext: {
print("두 번째 구독 : \($0)")
}).disposed(by: disposeBag)
print("----------------------------------------------------------")
let subject4 = AsyncSubject<Int>()
subject4.subscribe(onNext: {
print("첫 번째 구독 : \($0)")
}).disposed(by: disposeBag)
subject4.onNext(1)
subject4.onNext(2)
subject4.onNext(3)
subject4.onNext(4)
subject4.onCompleted()
print("----------------------------------------------------------")
}
}
다음 포스팅은 Relay에 대해 알아보겠습니다.
감사합니다~~
참고
'iOS 🖥️ > RxSwift' 카테고리의 다른 글
[RxSwift] Traits ( Single, Maybe, Completable ) (0) | 2024.01.18 |
---|---|
[RxSwift] Debounce, Throttle (0) | 2024.01.17 |
[RxCocoa] Driver (0) | 2024.01.17 |
[RxCocoa] Relay (2) | 2024.01.16 |
[ RxSwift ] Operator (0) | 2024.01.11 |