好了, 接下來是第三個部分。Subjects
學(xué)了之前內(nèi)容. 我們可能已經(jīng)發(fā)現(xiàn)了。之前學(xué)習(xí)的內(nèi)容都是 Observables
輸出事件的部分。我們可以訂閱他, 就能知道他輸出的事件了。但是我們還不能改變他。
Subject 也是一個 Observable
但是他是能夠同時輸入和輸出的。也就是說, 我們可以動態(tài)(強制)的在一個序列中發(fā)出信號。
let subject = PublishSubject<String>()
// 可以直接轉(zhuǎn)換,因為他也是一個 `Observable`
let observable: Observable<String> = subject
observable.subscribe(onNext: { (text) in
print(text)
}).addDisposableTo(disposeBag)
// 只要你想發(fā)出一個新的事件, 就可以用 onNext 方法
subject.onNext("Hey!")
subject.onNext("I'm back!")
onNext 是一個輸出事件的方法。最后控制臺會輸出
"Hey!"
"I'm back!"
Subject
到底有什么用呢? 為了很輕松的將 Rxswift 中聲明式的世界和我們平常的世界連接起來。讓我們在需要寫實現(xiàn)式的代碼的時候更 Rx
在一個純正的 Rx 的世界里。當(dāng)你需要有一個更完美的流的時候, 不用去管這個 Observable
是怎么實現(xiàn)的。這個東西我會另外的解釋。反正, 如果你需要, 大膽的用吧。
上面式關(guān)于 Subject 最基本的內(nèi)容。接下來我們學(xué)習(xí)一下怎么更好的使用 Subject
Hot?? vs Cold??
在第一篇文章中就已經(jīng)提到過了熱信號??和冷信號??。今天我們在深入的了解一點吧,因為 Subject 實際上是我們第一次接觸到真正的熱信號。
我們一定確定了,當(dāng)我們使用 create 創(chuàng)建一個 Observable 的時候, 由于沒有人訂閱他,所以她是不會發(fā)送消息的。只有被 subscribe(訂閱)之后才會開始發(fā)送消息出來。這就是我們叫它為冷信號??的原因。如果很不幸你忘了這個知識點。你可以回到第一篇文章去看看。熱信號?? 就是那種即使沒有被訂閱也會發(fā)出消息的信號, 這也是 subject
做的事情。
let subject = PublishSubject<String>()
let observable: Observable<String> = subject
// 這個信號還沒有被訂閱, 所以這個值不回被接受到
subject.onNext("Am I too early for the party?")
observable
.subscribe(onNext: { (text) in
print(text)
}).addDisposableTo(disposeBag)
// 這個值發(fā)出來的時候已經(jīng)有一個訂閱者了, 所以這個值會打印出來
subject.onNext("??????")
很簡單直接吧。如果在第一篇中你理解了冷信號的話, 理解熱信號也是很自然的事情。
Subject Types
常用的 Subject
有三種。 他們其實都差不多, 唯一的區(qū)別就是: 在訂閱之前, 它會干什么。
Publish Subject
在上面的例子中已經(jīng)說到了。 PublishSubject 會忽略掉在訂閱之前發(fā)出來的信號。
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ā)生了什么的時候, 就可以使用 PublishSubject
Replay Subjects
ReplaySubject 會將最后 n 個值發(fā)出來, 即使是訂閱發(fā)生之前的值。 這個 n 個值被被放在一個環(huán)從區(qū)里面。在這個例子中會緩有 3 個值被保留。
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ā)生了什么的時候, 我們就需要使用 ReplaySubject
了。
Behavior Subject
BehaviorSubject 只會重復(fù)最后一個值。 更其他的 Subject 的同, 他在創(chuàng)建的時候就需要給定一個初始值。
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)你只需要知道最后一個值的時候。就需要使用 BehaviorSubject
Binding
你可以把一個 Observable
和 Subject
綁定到一起。也就是說可以讓這個 Observable
將它的序列里的所有值都發(fā)送給這個 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)
有一個語法糖來簡化這些代碼。
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 不僅僅會傳遞值, 他也會把完成和錯誤都傳遞過來。這種情況下這個 Subject
就會被釋放。
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)
可以看到,在這個例子中,我們有一個視圖模型將 googleString
這個 subject
暴露出來。讓 ViewController
能夠訂閱。我們將這個 observable
綁定到這個 subject
上, 這樣我們就可以在網(wǎng)絡(luò)請求有結(jié)果的時候, 立馬將請求結(jié)果傳遞到這給 subject
。
Bonus: Variable
距離完完全全的 Rx 還差最后一點了。強行的獲取之前發(fā)送出來的值。
這就是為什么會有 Variable 這個東西了。Variable 是對 BehaviorSubject 的簡單包裝。可以看一下 它的實現(xiàn)是非常簡單的。但它卻非常的方便。
還是用一個小例子來說明這個問題吧。在這個例子中, 我們需要在任何時間都可以得到 "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)
你一定會愛上他的。這基本上就是 RxSwift 的簡單模式了。
看起來很簡單吧,但是別忘了,還是有很多的坑的。還是小心為上。下一篇文章我會講講: 怎么寫 Rxswift 最保險。
That's it!
你知道了太多了。剩下的就是 Subjects
了