
好了, 接下來是第三個(gè)部分。Subjects
學(xué)了之前內(nèi)容. 我們可能已經(jīng)發(fā)現(xiàn)了。之前學(xué)習(xí)的內(nèi)容都是 Observables 輸出事件的部分。我們可以訂閱他, 就能知道他輸出的事件了。但是我們還不能改變他。
Subject 也是一個(gè) Observable 但是他是能夠同時(shí)輸入和輸出的。也就是說, 我們可以動(dòng)態(tài)(強(qiáng)制)的在一個(gè)序列中發(fā)出信號(hào)。
let subject = PublishSubject<String>()
// 可以直接轉(zhuǎn)換,因?yàn)樗彩且粋€(gè) `Observable`
let observable: Observable<String> = subject
observable.subscribe(onNext: { (text) in
print(text)
}).addDisposableTo(disposeBag)
// 只要你想發(fā)出一個(gè)新的事件, 就可以用 onNext 方法
subject.onNext("Hey!")
subject.onNext("I'm back!")
onNext 是一個(gè)輸出事件的方法。最后控制臺(tái)會(huì)輸出
"Hey!"
"I'm back!"
Subject 到底有什么用呢? 為了很輕松的將 Rxswift 中聲明式的世界和我們平常的世界連接起來。讓我們?cè)谛枰獙憣?shí)現(xiàn)式的代碼的時(shí)候更 Rx
在一個(gè)純正的 Rx 的世界里。當(dāng)你需要有一個(gè)更完美的流的時(shí)候, 不用去管這個(gè) Observable 是怎么實(shí)現(xiàn)的。這個(gè)東西我會(huì)另外的解釋。反正, 如果你需要, 大膽的用吧。
上面式關(guān)于 Subject 最基本的內(nèi)容。接下來我們學(xué)習(xí)一下怎么更好的使用 Subject
Hot?? vs Cold??
在第一篇文章中就已經(jīng)提到過了熱信號(hào)??和冷信號(hào)??。今天我們?cè)谏钊氲牧私庖稽c(diǎn)吧,因?yàn)?Subject 實(shí)際上是我們第一次接觸到真正的熱信號(hào)。
我們一定確定了,當(dāng)我們使用 create 創(chuàng)建一個(gè) Observable 的時(shí)候, 由于沒有人訂閱他,所以她是不會(huì)發(fā)送消息的。只有被 subscribe(訂閱)之后才會(huì)開始發(fā)送消息出來。這就是我們叫它為冷信號(hào)??的原因。如果很不幸你忘了這個(gè)知識(shí)點(diǎn)。你可以回到第一篇文章去看看。熱信號(hào)?? 就是那種即使沒有被訂閱也會(huì)發(fā)出消息的信號(hào), 這也是 subject 做的事情。
let subject = PublishSubject<String>()
let observable: Observable<String> = subject
// 這個(gè)信號(hào)還沒有被訂閱, 所以這個(gè)值不回被接受到
subject.onNext("Am I too early for the party?")
observable
.subscribe(onNext: { (text) in
print(text)
}).addDisposableTo(disposeBag)
// 這個(gè)值發(fā)出來的時(shí)候已經(jīng)有一個(gè)訂閱者了, 所以這個(gè)值會(huì)打印出來
subject.onNext("??????")
很簡(jiǎn)單直接吧。如果在第一篇中你理解了冷信號(hào)的話, 理解熱信號(hào)也是很自然的事情。
Subject Types
常用的 Subject 有三種。 他們其實(shí)都差不多, 唯一的區(qū)別就是: 在訂閱之前, 它會(huì)干什么。
Publish Subject
在上面的例子中已經(jīng)說到了。 PublishSubject 會(huì)忽略掉在訂閱之前發(fā)出來的信號(hào)。
let subject = PublishSubject<String>()
let observable: Observable<String> = subject
subject.onNext("Ignored...")
observable.subscribe(onNext: { (text) in
print(text)
}).addDisposableTo(disposeBag)
subject.onNext("Printed!")
當(dāng)你只關(guān)注你訂閱之后發(fā)生了什么的時(shí)候, 就可以使用 PublishSubject
Replay Subjects
ReplaySubject 會(huì)將最后 n 個(gè)值發(fā)出來, 即使是訂閱發(fā)生之前的值。 這個(gè) n 個(gè)值被被放在一個(gè)環(huán)從區(qū)里面。在這個(gè)例子中會(huì)緩有 3 個(gè)值被保留。
let subject = ReplaySubject<String>.create(bufferSize: 3)
let observable: Observable<String> = subject
subject.onNext("Not printed!")
subject.onNext("Printed")
subject.onNext("Printed!")
subject.onNext("Printed!")
observable.subscribe(onNext: { (text) in
print(text)
}).addDisposableTo(disposeBag)
subject .onNext("Printed!")
當(dāng)我們需要知道訂閱之前發(fā)生了什么的時(shí)候, 我們就需要使用 ReplaySubject 了。
Behavior Subject
BehaviorSubject 只會(huì)重復(fù)最后一個(gè)值。 更其他的 Subject 的同, 他在創(chuàng)建的時(shí)候就需要給定一個(gè)初始值。
let subject = BehaviorSubject<String>(value: "Initial value")
let observable: Observable<String> = subject
subject.onNext("Not printed!")
subject.onNext("Not printed!")
subject.onNext("Printed!")
observable.subscribe(onNext: { (text) in
print(text)
}).addDisposableTo(disposeBag)
subject.onNext("Printed!")
當(dāng)你只需要知道最后一個(gè)值的時(shí)候。就需要使用 BehaviorSubject
Binding
你可以把一個(gè) Observable 和 Subject 綁定到一起。也就是說可以讓這個(gè) Observable 將它的序列里的所有值都發(fā)送給這個(gè) Subject
let subject = PublishSubject<String>()
let observable = Observable<String>.just("I'm being passed around ??")
subject.subscribe(onNext: { (text) in
print(text)
}).addDisposableTo(disposeBag)
observable.subscribe { (event) in
subject.on(event)
}.addDisposableTo(disposeBag)
有一個(gè)語(yǔ)法糖來簡(jiǎn)化這些代碼。
let subject = PublishSubject<String>()
let observable = Observable<String>.just("I'm being passed around ??")
subject.subscribe(onNext: { (text) in
print(text)
}).addDisposableTo(disposeBag)
observable.bind(to: subject).addDisposableTo(disposeBag)
輸出
I'm being passed around ??
Warning
Binding 不僅僅會(huì)傳遞值, 他也會(huì)把完成和錯(cuò)誤都傳遞過來。這種情況下這個(gè) Subject 就會(huì)被釋放。
Quick Example
還是把第一篇文章中的 Demo 稍微修改一下吧。
import Foundation
import RxCocoa
import RxSwift
final class GoogleModel {
let googleString = BehaviorSubject<String>(value: "")
private let disposeBag = DisposeBag()
func fetchNetString() {
let observable = Observable<String>.create { (observer) -> Disposable in
let session = URLSession.shared
let task = session.dataTask(with: URL(string: "https://www.google.com")!, completionHandler: { (data, response, error) in
DispatchQueue.main.async {
if let err = error {
observer.onError(err)
} else {
let googleString = NSString(data: data!, encoding: 1) as String?
observer.onNext(googleString!)
observer.onCompleted()
}
}
})
task.resume()
return Disposables.create{
task.cancel()
}
}
// Bind the observable to the subject
observable.bind(to: googleString).addDisposableTo(disposeBag)
}
}
// Bind the observable to the subject
observable.bind(to: googleString).addDisposableTo(disposeBag)
可以看到,在這個(gè)例子中,我們有一個(gè)視圖模型將 googleString 這個(gè) subject 暴露出來。讓 ViewController 能夠訂閱。我們將這個(gè) observable 綁定到這個(gè) subject 上, 這樣我們就可以在網(wǎng)絡(luò)請(qǐng)求有結(jié)果的時(shí)候, 立馬將請(qǐng)求結(jié)果傳遞到這給 subject。
Bonus: Variable
距離完完全全的 Rx 還差最后一點(diǎn)了。強(qiáng)行的獲取之前發(fā)送出來的值。
這就是為什么會(huì)有 Variable 這個(gè)東西了。Variable 是對(duì) BehaviorSubject 的簡(jiǎn)單包裝。可以看一下 它的實(shí)現(xiàn)是非常簡(jiǎn)單的。但它卻非常的方便。
還是用一個(gè)小例子來說明這個(gè)問題吧。在這個(gè)例子中, 我們需要在任何時(shí)間都可以得到 "googleString" "當(dāng)前" 的值。
let googleString = Variable("currentString")
// get
print(googleString.value)
// set
googleString.value = "newString"
// 訂閱
googleString.asObservable().subscribe(onNext: { (text) in
print(text)
}).addDisposableTo(disposeBag)
你一定會(huì)愛上他的。這基本上就是 RxSwift 的簡(jiǎn)單模式了。
看起來很簡(jiǎn)單吧,但是別忘了,還是有很多的坑的。還是小心為上。下一篇文章我會(huì)講講: 怎么寫 Rxswift 最保險(xiǎn)。
That's it!
你知道了太多了。剩下的就是 Subjects 了