RxSwift(六)-- RxSwift使用介绍Subject

Subject介绍

从之前的文章RxSwift(三)-- RxSwift使用介绍Observable的创建中,我们可以知道,当我们需要创建一个 Observable的时候,要预先将要发出的数据都准备好,等到有人订阅它时再将数据通过 Event发出去。
不过,有时我们也希望 Observable 在运行时能动态地“获得”或者说“产生”出一个新的数据,再通过 Event 发送出去。比如:订阅一个输入框的输入内容,当用户每输入一个字后,这个输入框关联的 Observable 就会发出一个带有输入内容的 Event,通知给所有订阅者。
这个时候,我们要介绍的Subject就登场了。

Subject基本介绍

  1. Subject它既是订阅者,也是Observable序列:
  • 因为它能够动态地接收新的值,所以它是订阅者。
  • 因为Subject有了新的值以后,会通过Event事件将新的值发出来给到订阅者,所以它又是一个Observable序列。
  1. RxSwiftSubject的分类是:PublishSubjectBehaviorSubjectReplaySubjectAsyncSubjectBehaviorRelay,它们之间既有各自的特点,也有相同之处:
  • 它们都是Observable序列,订阅者都能收到它们发出的新的Event
  • 等到Subject发出.complete或者.errorEventSubject也就结束了,也就不会再发出.next事件了。
  • 如果在Subject结束后再订阅的订阅者,也可以收到Subject发出的一条.complete或者.error事件,用来告诉订阅者已经结束了。
  • 它们之间的最大区别是:当一个新的订阅者刚订阅它的时候,能不能收到 Subject 以前发出过的旧 Event,如果能的话又能收到多少个。
  1. Subject的常用方法:
  • onNext(:):是 on(.next(:)) 的简便写法。该方法相当于 Subject 接收到一个 .next 事件。
  • onError(:):是 on(.error(:)) 的简便写法。该方法相当于 Subject 接收到一个 .error 事件。
  • onCompleted():是 on(.completed) 的简便写法。该方法相当于 subject 接收到一个 .completed 事件。

PublishSubject

  1. 介绍
  • PublishSubject不需要初始值就能创建(也就是可以为空)。
  • PublishSubject的订阅者从他们开始订阅的时间点起,可以收到订阅后 Subject 发出的新 Event事件,而不会收到他们在订阅前已发出的 Event事件。
  1. 代码展示
// 创建一个PublishSubject
        let subject = PublishSubject<String>()
        
        // 由于当前没有任何订阅者,所以这条信息不会输出到控制台
        subject.onNext("111")
        
        // 第1次订阅subject
        subject.subscribe(onNext: { string in
            print("第1次订阅:", string)
        }, onCompleted:{
            print("第1次订阅:onCompleted")
        }).disposed(by: disposeBag)
        
        // 当前有1个订阅,则该信息会输出到控制台
        subject.onNext("222")
       
        // 第2次订阅subject
        subject.subscribe(onNext: { string in
            print("第2次订阅:", string)
        }, onCompleted:{
            print("第2次订阅:onCompleted")
        }).disposed(by: disposeBag)
        
        // 当前有2个订阅,则该信息会输出到控制台
        subject.onNext("333")
        
        // 让subject结束
        subject.onCompleted()
        
        // subject完成后会发出.next事件了。
        subject.onNext("444")
        // subject完成后它的所有订阅(包括结束后的订阅),都能收到subject的.completed事件,
        subject.subscribe(onNext: { string in
            print("第3次订阅:", string)
        }, onCompleted:{
            print("第3次订阅:onCompleted")
        }).disposed(by: disposeBag)

运行结果:

第1次订阅: 222
第1次订阅: 333
第2次订阅: 333
第1次订阅:onCompleted
第2次订阅:onCompleted
第3次订阅:onCompleted

BehaviorSubject

  1. 介绍
  • BehaviorSubject 需要通过一个默认初始值来创建。
  • 当一个订阅者来订阅它的时候,这个订阅者会立即收到 BehaviorSubjects 上一个发出的 event事件,如果还没有收到任何数据,则会发出一个默认值。之后就跟PublishSubject正常的情况一样,它也会接收到 BehaviorSubject 之后发出的新的 event事件。
  1. 代码展示
// 创建一个BehaviorSubject
        let subject = BehaviorSubject(value: "111")
        
        // 第1次订阅subject
        subject.subscribe { event in
            print("第1次订阅:", event)
            }.disposed(by: disposeBag)
        
        // 发送next事件
        subject.onNext("222")
        
        // 发送error事件
        subject.onError(NSError(domain: "local", code: 0, userInfo: nil))
        
        // 第2次订阅subject
        subject.subscribe { event in
            print("第2次订阅:", event)
            }.disposed(by: disposeBag)

运行结果:

第1次订阅: next(111)
第1次订阅: next(222)
第1次订阅: error(Error Domain=local Code=0 "(null)")
第2次订阅: error(Error Domain=local Code=0 "(null)")

ReplaySubject

  1. 介绍
  • ReplaySubject 在创建时候需要设置一个 bufferSize,表示它对于它发送过的 event 的缓存个数。
  • 比如一个 ReplaySubjectbufferSize 设置为 2,它发出了 3 个 .nextevent,那么它会将后两个(最近的两个)event 给缓存起来。此时如果有一个 subscriber 订阅了这个 ReplaySubject,那么这个 subscriber 就会立即收到前面缓存的两个 .nextevent
  • 如果一个 subscriber 订阅已经结束的 ReplaySubject,除了会收到缓存的 .nextevent 外,还会收到那个终结的 .error 或者 .completeevent
  1. 代码展示
//创建一个bufferSize为2的ReplaySubject
        let subject = ReplaySubject<String>.create(bufferSize: 2)
        
        //连续发送3个next事件
        subject.onNext("111")
        subject.onNext("222")
        subject.onNext("333")
        
        //第1次订阅subject
        subject.subscribe { event in
            print("第1次订阅:", event)
            }.disposed(by: disposeBag)
        
        //再发送1个next事件
        subject.onNext("444")
        
        //第2次订阅subject
        subject.subscribe { event in
            print("第2次订阅:", event)
            }.disposed(by: disposeBag)
        
        //让subject结束
        subject.onCompleted()
        
        //第3次订阅subject
        subject.subscribe { event in
            print("第3次订阅:", event)
            }.disposed(by: disposeBag)

运行结果:

第1次订阅: next(222)
第1次订阅: next(333)
第1次订阅: next(444)
第2次订阅: next(333)
第2次订阅: next(444)
第1次订阅: completed
第2次订阅: completed
第3次订阅: next(333)
第3次订阅: next(444)
第3次订阅: completed

BehaviorRelay

  1. 介绍
  • BehaviorRelay 是作为 Variable 的替代者出现的。它的本质其实也是对 BehaviorSubject 的封装,所以它也必须要通过一个默认的初始值进行创建。
  • BehaviorRelay 具有 BehaviorSubject 的功能,能够向它的订阅者发出上一个 event 以及之后新创建的 event。
  • BehaviorSubject 不同的是,不需要也不能手动给 BehaviorReply 发送 completed 或者 error 事件来结束它(BehaviorRelay 在销毁时也不会自动发送 .completeevent)。
  • BehaviorRelay 有一个 value属性,我们通过这个属性可以获取最新值。而通过它的 accept() 方法可以对值进行修改。
  1. 代码展示
//创建一个初始值为111的BehaviorRelay
        let subject = BehaviorRelay<String>(value: "111")
        
        //修改value值
        subject.accept("222")
        
        //第1次订阅
        subject.asObservable().subscribe {
            print("第1次订阅:", $0)
            }.disposed(by: disposeBag)
        
        //修改value值
        subject.accept("333")
        
        //第2次订阅
        subject.asObservable().subscribe {
            print("第2次订阅:", $0)
            }.disposed(by: disposeBag)
        
        //修改value值
        subject.accept("444")

运行结果:

第1次订阅: next(222)
第1次订阅: next(333)
第2次订阅: next(333)
第1次订阅: next(444)
第2次订阅: next(444)
  1. 另外,如果想将新值合并到原值上,可以通过 accept() 方法与 value 属性配合来实现。(这个常用在表格上拉加载功能上,BehaviorRelay 用来保存所有加载到的数据)
//创建一个初始值为包含一个元素的数组的BehaviorRelay
        let subject = BehaviorRelay<[String]>(value: ["1"])
        
        //修改value值
        subject.accept(subject.value + ["2", "3"])
        
        //第1次订阅
        subject.asObservable().subscribe {
            print("第1次订阅:", $0)
            }.disposed(by: disposeBag)
        
        //修改value值
        subject.accept(subject.value + ["4", "5"])
        
        //第2次订阅
        subject.asObservable().subscribe {
            print("第2次订阅:", $0)
            }.disposed(by: disposeBag)
        
        //修改value值
        subject.accept(subject.value + ["6", "7"])

运行结果:

第1次订阅: next(["1", "2", "3"])
第1次订阅: next(["1", "2", "3", "4", "5"])
第2次订阅: next(["1", "2", "3", "4", "5"])
第1次订阅: next(["1", "2", "3", "4", "5", "6", "7"])
第2次订阅: next(["1", "2", "3", "4", "5", "6", "7"])

AsyncSubject

  1. 介绍
  • 一个AsyncSubject只在原始Observable完成后,发射来自原始Observable的最后一个值。
  • 如果原始Observable没有发射任何值,AsyncObject也不发射任何值,它会把这最后一个值发射给任何后续的观察者。
  • 如果原始的Observable因为发生了错误而终止,AsyncSubject将不会发射任何数据,只是简单的向前传递这个错误通知。
  1. 代码展示
let subject = AsyncSubject<Int>()
        
        subject.onNext(1)
        
        subject.subscribe(onNext: { int in
            print("observerA: \(int)")
        }, onCompleted: {
            print("observerA: onCompleted")
        }).disposed(by: disposeBag)
        
        subject.onNext(2)
        
        subject.subscribe(onNext: { int in
            print("observerB: \(int)")
        }, onCompleted: {
            print("observerB: onCompleted")
        }).disposed(by: disposeBag)
        
        subject.onNext(3)
        
        subject.subscribe(onNext: { int in
            print("observerC: \(int)")
        }, onCompleted: {
            print("observerC: onCompleted")
        }).disposed(by: disposeBag)
        
        subject.onCompleted()
        
        subject.onNext(4)
        
        subject.subscribe(onNext: { int in
            print("observerD: \(int)")
        }, onCompleted: {
            print("observerD: onCompleted")
        }).disposed(by: disposeBag)

运行结果:

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

推荐阅读更多精彩内容