Subject는 Observable, Observer의 역할을 모두 할 수 있습니다.
이전에 배운 Observable은 unicast방식이지만, Subject는 multicast방식으로, 여러개의 observer를 subscribe 할 수 있습니다.
📌Subject와 Observable의 차이
Observable | Subject |
---|---|
단지 함수이며 state가 없음 | state가 있으며, data를 메모리에 저장함 |
각각의 옵저버에 대해 코드가 실행 | 같은 코드 실행, 모든 옵저버에 대해 오직 한번만 |
Data Producer | Date Producer and Consumer |
<용도> 하나의 Observer에 대한 간단한 Observable이 필요할 때 | <용도> 1. 자주 데이터를 저장하고 수정할때 2. Observable과 Observer 사이의 Proxy 역할 |
코드 예시
let disposeBag = DisposeBag()
let subject = PublishSubject<String>()
subject.onNext("Hi")
let o1 = subject.subscribe {print (">>1",$0)}
o1.disposed(by: disposeBag)
subject.onNext("a")
subject.onNext("b")
let o2 = subject.subscribe{print(">>2",$0)}
o2.disposed(by: disposeBag)
subject.onNext("c")
subject.onNext("d")
출력 결과
>>1 next(a)
>>1 next(b)
>>1 next(c)
>>2 next(c)
>>1 next(d)
>>2 next(d)
설명
PublishSubject는 구독한 시점 이후에 발생한 이벤트만 전달받는다.
따라서 o1이 구독한 시점 이전에 발생한 "Hi"는 전달되지 않으며,
o2가 구독한 시점 이전에 발생한 "Hi", "a", "b"는 전달되지 않는다.
코드 예시
let disposeBag = DisposeBag()
let subject = BehaviorSubject<String>(value: "start") //observer
subject.onNext("Hi")
let o1 = subject.subscribe {print (">>1",$0)}
o1.disposed(by: disposeBag)
subject.onNext("a")
subject.onNext("b")
let o2 = subject.subscribe{print(">>2",$0)}
o2.disposed(by: disposeBag)
subject.onNext("c")
subject.onNext("d")
출력 결과
>>1 next(Hi)
>>1 next(a)
>>1 next(b)
>>2 next(b)
>>1 next(c)
>>2 next(c)
>>1 next(d)
>>2 next(d)
설명
BehaviorSubject는 PublicSubject와 다르게 초기값을 가지고 생성된다.
따라서 o1이 구독한 시점 이전에 발생한 "Hi"를 전달받으며,
o2가 구독한 시점 이전에 발생한 "b"를 전달받는다.
코드 예시
let disposeBag = DisposeBag()
let rsubject = ReplaySubject<String>.create(bufferSize : 3)
rsubject.onNext("First")
rsubject.onNext("Second")
rsubject.onNext("Third")
rsubject.subscribe { event in
print(event)
}
rsubject.disposed(by: disposeBag)
rsubject.onNext("Fourth")
rsubject.onNext("Fifth")
출력 결과
"First"
"Second"
"Third"
"Fourth"
"Fifth"
설명
ReplySubject는 구독 전에 발생한 이벤트를 버퍼 사이즈만큼 버퍼에 저장한다.
따라서 구독 이후 버퍼에 있던 3개의 이벤트(First, Second, Thrid)가 전달되고, disposed로 인해 버퍼에 저장된 것들이 처분되므로 이후 Fourth, Fifth가 전달된다.
Relay는 RxCocoa 클래스를 이용합니다.
RxSwift는 Cocoa, UIKit에 대해선 알지 못합니다. 따라서 이를 사용하기 위해선 RxCocoa를 이용합니다.
Relay의 종류에는 PublishRelay, BehaviorRelay, ReplayRelay가 있으며 Wrapper 클래스입니다.
(Wrapper 클래스란 data->객체로 포장해주는 클래스를 의미합니다.)
📌Relay와 Subject의 차이
Relay | Subject |
---|---|
emit: accept 사용 | emit: onNext 사용 |
Dispose 되어야 종료 | .completed 또는 .error 이벤트 발생 시 구독 종료 |
next만 이벤트 전달 | next, error, completed 이벤트 전달 |
코드 예시
var BR = BehaviorRelay(value: " ")
var observable : Observable<String>{
return BR.asObservable()
}
BR.accept("event")
📌Subject와 Observable의 차이 실감하기
위의 Label 3개는 Observable을 통해 생성된 랜덤 숫자이고, 아래의 Label 3개는 Subject를 통해 생성된 랜덤 숫자입니다.
Observable은 각각의 옵저버에 대해 코드가 독립적으로 실행되는 반면에, Subject는 같은 코드가 모두 동일하게 실행되기 때문에 Obervable의 Label들은 각각 랜덤한 숫자를 나타냈고, Subject의 Label들은 모두 동일한 숫자를 나타냈습니다.
@IBAction func RoleBtnClicked(_ sender: Any) {
let randomNumGenerator1 = Observable< Int >.create{ observer in
observer.onNext(Int.random(in: 0 ..< 100))
return Disposables.create()
}
randomNumGenerator1.subscribe(onNext: { (element) in
self.ob1.text = "\(element)"
}).dispose()
randomNumGenerator1.subscribe(onNext: { (element) in
self.ob2.text = "\(element)"
})
randomNumGenerator1.subscribe(onNext: { (element) in
self.ob3.text = "\(element)"
})
let randomNumGenerator2 = BehaviorSubject(value: 0)
randomNumGenerator2.onNext(Int.random(in: 0..< 100))
randomNumGenerator2.subscribe(onNext: { (element) in
self.sub1.text = "\(element)"
})
randomNumGenerator2.subscribe(onNext: { (element) in
self.sub2.text = "\(element)"
})
randomNumGenerator2.subscribe(onNext: { (element) in
self.sub3.text = "\(element)"
})
}
📌RxCocoa 실감하기
switch의 On/Off 상태에 따라 Label을 변경시켰습니다.
RxCocoa는 RxSwift와 다르게 UIKit와 연결할 수 있기 때문에, UIKit의 switch의 상태에 따라 이벤트를 발생시킬 수 있습니다.
var BR = BehaviorRelay(value: " ")
@IBOutlet weak var switchLabel: UILabel!
@IBOutlet weak var rxSwitch: UISwitch!
rxSwitch.rx.isOn.subscribe { (enable) in
self.switchLabel.text = enable.element! ? "On" : "Off"
self.BR.accept(self.switchLabel.text ?? " ")
}.disposed(by: disposebag)