RxSwift For Dummies ?? Part3

image.png

好了, 接下来是第三个部分。Subjects

学了之前内容. 我们可能已经发现了。之前学习的内容都是 Observables 输出事件的部分。我们可以订阅他, 就能知道他输出的事件了。但是我们还不能改变他。

Subject 也是一个 Observable 但是他是能够同时输入和输出的。也就是说, 我们可以动态(强制)的在一个序列中发出信号。

        let subject = PublishSubject<String>()
        // 可以直接转换,因为他也是一个 `Observable`
        let observable: Observable<String> = subject
        observable.subscribe(onNext: { (text) in
            print(text)
        }).addDisposableTo(disposeBag)
        // 只要你想发出一个新的事件, 就可以用 onNext 方法 
        subject.onNext("Hey!")
        subject.onNext("I'm back!")

onNext 是一个输出事件的方法。最后控制台会输出

"Hey!"
"I'm back!"

Subject 到底有什么用呢? 为了很轻松的将 Rxswift 中声明式的世界和我们平常的世界连接起来。让我们在需要写实现式的代码的时候更 Rx

在一个纯正的 Rx 的世界里。当你需要有一个更完美的流的时候, 不用去管这个 Observable 是怎么实现的。这个东西我会另外的解释。反正, 如果你需要, 大胆的用吧。

上面式关于 Subject 最基本的内容。接下来我们学习一下怎么更好的使用 Subject

Hot?? vs Cold??

在第一篇文章中就已经提到过了热信号??和冷信号??。今天我们在深入的了解一点吧,因为 Subject 实际上是我们第一次接触到真正的热信号。

我们一定确定了,当我们使用 create 创建一个 Observable 的时候, 由于没有人订阅他,所以她是不会发送消息的。只有被 subscribe(订阅)之后才会开始发送消息出来。这就是我们叫它为冷信号??的原因。如果很不幸你忘了这个知识点。你可以回到第一篇文章去看看。热信号?? 就是那种即使没有被订阅也会发出消息的信号, 这也是 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)
        // 这个值发出来的时候已经有一个订阅者了, 所以这个值会打印出来
        subject.onNext("??????")

很简单直接吧。如果在第一篇中你理解了冷信号的话, 理解热信号也是很自然的事情。

Subject Types

常用的 Subject 有三种。 他们其实都差不多, 唯一的区别就是: 在订阅之前, 它会干什么。

Publish Subject

在上面的例子中已经说到了。 PublishSubject 会忽略掉在订阅之前发出来的信号。

        let subject = PublishSubject<String>()
        let observable: Observable<String> = subject
        subject.onNext("Ignored...")
        observable.subscribe(onNext: { (text) in
            print(text)
        }).addDisposableTo(disposeBag)
        subject.onNext("Printed!")

当你只关注你订阅之后发生了什么的时候, 就可以使用 PublishSubject

Replay Subjects

ReplaySubject 会将最后 n 个值发出来, 即使是订阅发生之前的值。 这个 n 个值被被放在一个环从区里面。在这个例子中会缓有 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!")

当我们需要知道订阅之前发生了什么的时候, 我们就需要使用 ReplaySubject 了。

Behavior Subject

BehaviorSubject 只会重复最后一个值。 更其他的 Subject 的同, 他在创建的时候就需要给定一个初始值。

        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!")

当你只需要知道最后一个值的时候。就需要使用 BehaviorSubject

Binding

你可以把一个 ObservableSubject 绑定到一起。也就是说可以让这个 Observable 将它的序列里的所有值都发送给这个 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 上, 这样我们就可以在网络请求有结果的时候, 立马将请求结果传递到这给 subject。

Bonus: Variable

距离完完全全的 Rx 还差最后一点了。强行的获取之前发送出来的值。

这就是为什么会有 Variable 这个东西了。Variable 是对 BehaviorSubject 的简单包装。可以看一下 它的实现是非常简单的。但它却非常的方便。

还是用一个小例子来说明这个问题吧。在这个例子中, 我们需要在任何时间都可以得到 "googleString" "当前" 的值。

        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

原文地址

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351

推荐阅读更多精彩内容