RxSwift For Dummies ?? Part3

image.png

好了, 接下來是第三個(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è) ObservableSubject 綁定到一起。也就是說可以讓這個(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

原文地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容