RxSwift基礎(chǔ)之一 - Observables & Subjects

本文章內(nèi)部分圖片資源來自RayWenderlich.com

本文結(jié)合自己的理解來總結(jié)介紹一下RxSwift最基本的一些概念隆檀,當(dāng)然只這一篇是肯定介紹不完RxSwift的,這篇文算是起個(gè)頭绎狭,以后有時(shí)間時(shí)候則會(huì)繼續(xù)

簡(jiǎn)要概述

"Rx"是ReactiveX的縮寫鸽捻,它是目前比較流行比較火的一種開發(fā)方式的庫蜻展,"Rx"本身可以說是一種跨平臺(tái)的標(biāo)準(zhǔn)锥忿,不管是web還是移動(dòng)開發(fā),都能用"Rx"的思維和方法來完成你的工作寸认。
"Rx"有自己的社區(qū)論壇签财。除了RxSwift之外,還有類似RxJava, RxJS, RxKotlin, Rx.NET........都是基于不同的開發(fā)語言的Rx的庫偏塞,他們之間其實(shí)都是相通的唱蒸,都有相同的API,除了用的語言不同灸叼,所以說如果以后你使用別的語言做別的方面的開發(fā)神汹,同樣是可以用到相同的思維甚至相同的方法接口(除了語言不同)來編程。

這是Rx大家庭的主頁地址:reactivex
在里面可以看到Rx大家庭的所有成員如下

Rx Family

Rx自己的定義是這樣寫的:

ReactiveX is a library for composing asynchronous and event-based programs by using observable sequences.

Rx就是基于異步Event(事件)序列的響應(yīng)式編程古今,它可以簡(jiǎn)化異步編程方法屁魏,提供更優(yōu)雅的數(shù)據(jù)綁定,讓你可以時(shí)刻響應(yīng)新的數(shù)據(jù)同時(shí)順序地處理它們捉腥。
另外如果有使用MVVM開發(fā)模式的小伙伴通過RxSwift就可以獲得更加方便的數(shù)據(jù)綁定的方法氓拼,可以使得你的MVVM如虎添翼。

這是RxSwift在github上的地址:RXSwift
可以在這個(gè)地址頁面下方看到,如果你使用CocoaPods來引入RXSwift到你項(xiàng)目工程里時(shí)候披诗,它讓你要引入兩個(gè)庫,如下

pod 'RxSwift',    '~> 3.0'
pod 'RxCocoa',    '~> 3.0'

那么這個(gè)RxCocoa又是什么呢立磁?上面說到RxSwift它并純不是針對(duì)iOS開發(fā)呈队,而只是基于Swift語言的Rx標(biāo)準(zhǔn)實(shí)現(xiàn)接口庫,所以RxSwift里不包含任何Cocoa或者UI方面的類唱歧。
而RxCocoa就是基于RxSwift針對(duì)于iOS開發(fā)的一個(gè)庫宪摧,它通過Extension的方法給原生的比如UI控件添加了Rx的特性,使得你更容易訂閱和響應(yīng)這些控件的事件颅崩。
或者再極端一點(diǎn)說就是:使用Rx可以對(duì)你的程序里的事件傳遞響應(yīng)方法做到一統(tǒng)江湖几于,要將以前你常用的那些事件傳遞方法,比如delegate沿后、notification沿彭、target-action等等全部替換成Rx的"信號(hào)鏈"方式。

不過這篇文章里不會(huì)去寫RxCocoa的使用方法尖滚,而是主要介紹RxSwift中最基本的核心概念喉刘,只有從最根本的理解了才能使用的得心應(yīng)手。


Rx的根基Observables

Observable<T>這個(gè)類就是Rx框架的基礎(chǔ)漆弄,它的作用就是可以異步地產(chǎn)生一系列的Event(事件)睦裳,同時(shí)這些Event還可以攜帶數(shù)據(jù),它的泛型<T>就是用來指定這個(gè)Event攜帶的數(shù)據(jù)的類型撼唾。
即一個(gè)Observable<T>對(duì)象會(huì)隨著時(shí)間推移不定期地發(fā)出event(element : T)這樣一個(gè)東西廉邑。
不過首先需要有一個(gè)Observer訂閱者來訂閱Observable<T>,這樣這個(gè)訂閱者才能收到Observable<T>不時(shí)發(fā)出的Event倒谷。

如果你進(jìn)入RxSwift代碼里看看就能看到事件Event的定義如下:

public enum Event<Element> {
    /// Next element is produced.
    case next(Element)

    /// Sequence terminated with an error.
    case error(Swift.Error)

    /// Sequence completed successfully.
    case completed
}

可以看到Event的定義就是一個(gè)枚舉蛛蒙,也就是一個(gè)Observable是可以發(fā)出3種不同類型的Event事件:
next:next事件就是那個(gè)可以攜帶數(shù)據(jù)<T>的事件,可以說它就是一個(gè)“最正澈廾”的事件宇驾。
error:error事件表示一個(gè)錯(cuò)誤,它可以攜帶具體的錯(cuò)誤內(nèi)容猴伶,一旦Observable發(fā)出了error event课舍,則這個(gè)Observable就等于終止了,以后它再也不會(huì)發(fā)出event事件了他挎。
completed:completed事件表示Observable發(fā)出的事件正常地結(jié)束了筝尾,跟error一樣,一旦Observable發(fā)出了completed event办桨,則這個(gè)Observable就等于終止了筹淫,以后它再也不會(huì)發(fā)出event事件了。

Observable<Int>

從上圖可以看出Observable就相當(dāng)于一個(gè)sequence(序列)呢撞,它是基于時(shí)間的损姜,圖片中的三個(gè)圈就相當(dāng)于在三個(gè)不同的時(shí)間點(diǎn)上發(fā)出的Event事件饰剥。因?yàn)檫@個(gè)Observable 的泛型是Int:Observable<Int>,則表示它發(fā)出的Event攜帶的數(shù)據(jù)的數(shù)據(jù)類型是Int摧阅,就好像圖上的一樣汰蓉,三個(gè)Event攜帶的數(shù)據(jù)分別是數(shù)字1、2和3棒卷。

Observable end by Completed Event
Observable end by Error Event

上面兩個(gè)圖分別表示了兩個(gè)Observable分別以Completed Event和Error Event為終結(jié)顾孽,一旦Observable終結(jié)了,則它以后再也不會(huì)發(fā)出新的Event 了比规。


創(chuàng)建Observable

我們來寫實(shí)際寫一些代碼若厚,來創(chuàng)建Observable和訂閱它。你可以不用創(chuàng)建一個(gè)App項(xiàng)目工程蜒什,創(chuàng)建一個(gè)Xcode提供的playground項(xiàng)目即可测秸。另外不管你是創(chuàng)建app還是playground,都請(qǐng)先導(dǎo)入RxSwift這個(gè)庫T殖!F蚍狻!

我們來創(chuàng)建幾個(gè)Observable岗憋,Observable提供了just,of,from方法來通過默認(rèn)值創(chuàng)建一個(gè)Observable肃晚,如下

// 1
let observable: Observable<Int> = Observable<Int>.just(1)
// 2
let observable2 = Observable.of("One", "Two", "Three")
// 3
let observable3 = Observable.from([1, 2, 3])

我們分別通過三個(gè)方法來創(chuàng)建了三個(gè)observable對(duì)象
1.第一個(gè)通過just()方法,這個(gè)方法通過傳入一個(gè)默認(rèn)值來初始化仔戈」卮可以看到我們顯式地標(biāo)注出了observable的類型為Observable<Int>,指定了這個(gè)Observable所發(fā)出的事件攜帶的數(shù)據(jù)類型必須是Int類型的监徘。

2.第二個(gè)通過of()方法創(chuàng)建晋修,這個(gè)方法可以接受可變數(shù)量的參數(shù),不過注意他們一定要是同類型的凰盔,可以看到我并沒有顯式地聲明出Observable2的泛型類型墓卦,因?yàn)镾wift會(huì)自動(dòng)推斷類型,它看到我傳入的element全是String類型户敬,便默認(rèn)地認(rèn)為Observable2的類型為Observable<String>落剪。你可以按住Alt鍵同時(shí)鼠標(biāo)點(diǎn)擊observable2,看看它是不是真的是Observable<String>


observable2類型

3.第三種方法通過from()創(chuàng)建尿庐,它需要一個(gè)數(shù)組參數(shù)忠怖,而數(shù)據(jù)里的元素就會(huì)被當(dāng)做這個(gè)observable3所發(fā)出event攜帶的數(shù)據(jù)內(nèi)容,也就是用of()方法創(chuàng)建和把of()方法所有元素放在一個(gè)數(shù)組里再傳入from()方法創(chuàng)建的效果是一樣的抄瑟。你也可以按住Alt看看observable3的類型會(huì)被推斷為Observable<Int>而不是Observable<Array>


observable3類型

訂閱Observable

光有了Observable還不行凡泣,還需要有人來訂閱它,它才能發(fā)出Event,不然沒有理它它的Event發(fā)給誰聽呢?
在上面創(chuàng)建Obeservale的代碼下面加入如下代碼:

func subscribeOne() {
  print("------------ subscribe one ------------")
  observable.subscribe { event in
    print(event)
  }
  
  print("------------ subscribe two ------------")
  observable2.subscribe { event in
    print(event)
  }
  
  print("------------ subscribe three ------------")
  observable3.subscribe { event in
    print(event)
  }
}

記得要調(diào)用一下這個(gè)測(cè)試方法subscribeOne() 鞋拟。
很簡(jiǎn)單骂维,我們通過subscribe()訂閱了之前創(chuàng)建的3個(gè)Observable對(duì)象,這個(gè)block的回調(diào)參數(shù)就是被發(fā)出的event事件贺纲,我們直接print打印出來看看結(jié)果是什么:


subscribeOne() print result

可以看到我們?cè)诔跏蓟疧bservable對(duì)象時(shí)候設(shè)置的默認(rèn)值都被順序地通過.next事件發(fā)送了出來席舍,當(dāng)Observable對(duì)象的初始數(shù)據(jù)都發(fā)送完了,它還會(huì)自動(dòng)地發(fā)送一個(gè).completed事件出來哮笆。

但是注意此時(shí)打印出來的是event是Event類型,比如上面看到的next(1)汰扭,next(One)等等稠肘,我們?nèi)绻@取到這個(gè)事件里的數(shù)據(jù)呢?很簡(jiǎn)單萝毛,通過event.element即可獲取到项阴。但是這樣不是有點(diǎn)麻煩么,每次都要去event里讀取這個(gè)屬性笆包,所以RxSwift還提供了另一個(gè)subscribe方法如下:

func subscribeTwo() {
  observable3.subscribe(onNext: { element in
    print(element)
  }, onError: { error in
    print(error)
  }, onCompleted: { 
    print("completed")
  }, onDisposed: {
    
  })
}

先將上面調(diào)用subscribeOne() 代碼給注釋掉环揽,再調(diào)用一次subscribeTwo()看看結(jié)果


subscribeTwo() print result

可以看到這個(gè)方法其實(shí)就是將event給分類了,通過不同的block進(jìn)行回調(diào)庵佣,.next就通過onNext回調(diào)歉胶,.error的就通過onError回調(diào)椿争,同時(shí)它會(huì)把event攜帶的數(shù)據(jù)直接解包出來作為參數(shù)直接回調(diào)回來躲查,所以我們print(element)打印出來的結(jié)果就是.next事件所攜帶的數(shù)據(jù)了轧粟。
有人可能注意到最后有一個(gè)onDisposed的block蛇券,這個(gè)稍后會(huì)講到板壮,暫時(shí)可以不用管摔认。

PS: subscribe()方法的onNext原押、onError哈恰、onCompleted和onDisposed這四個(gè)回調(diào)block參數(shù)都是有默認(rèn)值的派哲,即它們都是可選的臼氨,你可以只設(shè)置onNext而不管其他的情況,比如:observable.subscribe( onNext: { print($0) } )芭届。


垃圾回收Dispose

一個(gè)Observable被創(chuàng)建出來后它不會(huì)馬上就開始被激活從而發(fā)出Event储矩,而是要等到它被某個(gè)人訂閱了才會(huì)激活它,激活之后一直等到Observable發(fā)出了.error或者.completed的event后褂乍,它才被終結(jié)椰苟。同時(shí)你也可以手動(dòng)的取消一個(gè)訂閱的行為,當(dāng)你覺得這個(gè)訂閱結(jié)束了不再需要了就需要調(diào)用對(duì)應(yīng)的方法把這個(gè)訂閱給銷毀掉树叽,有點(diǎn)類似以前的內(nèi)存管理舆蝴,否則可能會(huì)引起內(nèi)存泄漏。
當(dāng)你對(duì)一個(gè)Observable調(diào)用訂閱方法的時(shí)候,這個(gè)方法其實(shí)是有返回值的洁仗,返回值的類型是Disposable层皱,從它的類型名字意思就可以看出,這個(gè)類型是可丟棄可銷毀的赠潦。如果你進(jìn)入RxSwift的代碼里看一看:

/// Respresents a disposable resource.
public protocol Disposable {
    /// Dispose resource.
    func dispose()
}

Disposable其實(shí)就是一個(gè)protocol協(xié)議叫胖,而這個(gè)協(xié)議里只有一個(gè)方法就是dispose(),所以要取消一個(gè)訂閱行為這就要用到Disposable協(xié)議里的dispose()方法即可她奥。
方法如下:

func disposeOne() {
  
  // 1
  let subscription = observable.subscribe { event in
    print(event)
  }
  // 2
  subscription.dispose()
  
}

1.訂閱我們最開始創(chuàng)建過的observable對(duì)象瓮增,并且使用一個(gè)叫subscription的常量來接收訂閱方法的返回值
2.對(duì)這個(gè)訂閱行為調(diào)用dispose()方法
注意,如果一個(gè)訂閱行為被dispose了哩俭,那么之后observable如果再發(fā)出event绷跑,這個(gè)已經(jīng)dispose的訂閱就收不到消息了(雖然當(dāng)前例子中的observable不會(huì)再發(fā)出event了)

除了世界使用dispose()方法之外,還有一個(gè)平時(shí)更常用dispose訂閱的模式凡资,是使用一個(gè)叫DisposeBag的對(duì)象來管理一堆訂閱行為的銷毀砸捏,代碼如下

func disposeTwo() {
  
  // 1
  let disposeBag = DisposeBag()
  
  // 2
  observable2.subscribe { event in
    print(event)
  }.disposed(by: disposeBag)
  
  // 3
 observable3.subscribe { event in
    print(event)
  }.disposed(by: disposeBag)
  
}

1.創(chuàng)建一個(gè)DisposeBag對(duì)象,可以把它看成一個(gè)垃圾袋隙赁,把用過的訂閱行為都丟進(jìn)去垦藏,然后這個(gè)disposeBag就會(huì)在自己快要dealloc的時(shí)候?qū)λ锩娴乃杏嗛喰袨槎颊{(diào)用dispose()方法。
2.訂閱我們最開始創(chuàng)建的observable2伞访,緊接著就把它丟進(jìn)我們的“垃圾袋”里掂骏。
3.同理2,對(duì)最開始創(chuàng)建的observable3做同樣的操作厚掷。

好的說完了Dispose芭挽,我們現(xiàn)在再想想前面那個(gè)subcribe()方法里,有一個(gè)我們還沒有解釋的onDisposed的block的作用是什么蝗肪。不過我感覺現(xiàn)在也不用怎么解釋你也應(yīng)該知道了袜爪,它就是在這個(gè)訂閱行為被dispose之后會(huì)回調(diào)的block啦!


其他創(chuàng)建方法

Observable初始化除了上面提到的just,of,from之外薛闪,還有可以通過empty()創(chuàng)建一個(gè)空內(nèi)容的Observable辛馆,也能通過never()方法創(chuàng)建一個(gè)永遠(yuǎn)不會(huì)發(fā)出Event的Observable,還能通過range()方法指定起始和結(jié)束數(shù)值豁延,來通過值的范圍創(chuàng)建一個(gè)用這個(gè)范圍內(nèi)所有值作為初始值的Observable昙篙。

另外有兩個(gè)比較值得一說的就是Observable創(chuàng)建方法,一個(gè)就叫create()诱咏,一個(gè)叫deferred():

create()方法

Observable 可通過create()方法創(chuàng)建苔可,create方法接受一個(gè)block形式的參數(shù),這個(gè)block任務(wù)就是處理一個(gè)訂閱來了的處理情況袋狞。直接看代碼更簡(jiǎn)單:

func create() {
  // 1
  let ob = Observable<String>.create{observer in
    // 2
    observer.onNext("a")
    // 3
    observer.onCompleted()
    // 4
    return Disposables.create()
  }
  
  // 5
  ob.subscribe {
    print($0)
  }
}

1.通過create創(chuàng)建一個(gè)泛型為String的Observable對(duì)象焚辅,注意看他的block參數(shù)映屋,這個(gè)block有一個(gè)回調(diào)參數(shù)observer就是訂閱這個(gè)Observable對(duì)象的訂閱者,可以理解為當(dāng)一個(gè)訂閱者訂閱這個(gè)Observable對(duì)象的時(shí)候同蜻,就會(huì)將訂閱者作為參數(shù)傳入這個(gè)block來執(zhí)行一些內(nèi)容棚点。
2.我們對(duì)訂閱者發(fā)出了.next事件,且攜帶了一個(gè)數(shù)據(jù)a
3.對(duì)訂閱者發(fā)出了.completed事件
4.最后因?yàn)橐粋€(gè)訂閱行為會(huì)有一個(gè)Disposable類型的返回值湾蔓,所以在結(jié)尾一定要returen一個(gè)Disposable
5.訂閱測(cè)試

看一看這個(gè)測(cè)試方法運(yùn)行結(jié)果:


create() print output


deferred()方法

deferred()創(chuàng)建方法瘫析,使用這個(gè)方法創(chuàng)建Observable對(duì)象相當(dāng)于是一個(gè)Observable工廠,deferred()方法需要傳入一個(gè)block來執(zhí)行延遲創(chuàng)建的行為默责,這個(gè)block里就是真正的實(shí)例化對(duì)象的地方贬循。為了更容易理解就直接上代碼:

func deferredTest() {
  
  // 1
  var chinese = false
  
  // 2
  let factory : Observable<String> = Observable.deferred {
    
    // 3
    chinese = !chinese
    
    // 4
    if chinese {
      return Observable.of("一", "二", "三")
    }else {
      return Observable.of("One", "Two", "Three")
    }
    
  }

  // 5
  let disposeBag = DisposeBag()
  
  // 6
  factory.subscribe { event in
    print("1:", event)
  }.disposed(by: disposeBag)
  // 7
  factory.subscribe { event in
    print("2:", event)
  }.disposed(by: disposeBag)
}

1.我們創(chuàng)建了一個(gè)Bool類型的變量來表示是否為中文
2.我們創(chuàng)建了一個(gè)Observable工廠,雖然它對(duì)外看起來更其他Observable沒什么不同桃序,就是一個(gè)Observable<String>類型杖虾。但是我們通過deferred()方法將它的初始化給延遲了,通過傳入的block來做Observable實(shí)例的初始化并且返回葡缰。
3.我們先翻轉(zhuǎn)chinese,讓每次執(zhí)行這個(gè)block時(shí)候都不同
4.我們根據(jù)chinese標(biāo)示來選擇性的創(chuàng)建是包含中文還是英文的1忱反、2泛释、3值的Observable實(shí)例對(duì)象并且返回。

至此我們創(chuàng)建了一個(gè)Observable工廠温算,也就是說你每次調(diào)用factory獲取到的是不同的Observable對(duì)象怜校,接下來的代碼就是用來驗(yàn)證的。

5.創(chuàng)建了一個(gè)DisposeBag
6.訂閱factory注竿,并且打印出來event茄茁,同時(shí)通過打印一個(gè)"1"表示這是第一個(gè)訂閱的結(jié)果
7.同理再次訂閱factory,打印結(jié)果通過2來區(qū)分巩割。

現(xiàn)在來調(diào)用一次我們新寫的這個(gè)deferredTest()方法裙顽,看看輸出結(jié)果是什么:

deferredTest() print output

可以看到我們雖然兩次都是訂閱了factory,但其實(shí)訂閱的真是Observable對(duì)象是不同的宣谈,一個(gè)發(fā)出Event的值是中文的愈犹,一個(gè)發(fā)出的Event的值是英文的。






Subjects

接下來進(jìn)入這篇的第二個(gè)主題就是Subjects闻丑。
你可能會(huì)發(fā)現(xiàn)上面的說的Observable有個(gè)問題就是:我們?cè)趧?chuàng)建一個(gè)Observable的時(shí)候就要預(yù)先將它以后要發(fā)出的Event所攜帶的數(shù)據(jù)都準(zhǔn)備好漩怎,等到有人訂閱它再將數(shù)據(jù)通過Event發(fā)出去。
而實(shí)際情況通常是需要Observable在運(yùn)行時(shí)動(dòng)態(tài)地“收到”或者說“產(chǎn)生”出一個(gè)新的數(shù)據(jù)嗦嗡,再通過Event發(fā)送出去勋锤。也就是這些數(shù)據(jù)不是事先定好的,而已在程序運(yùn)行的時(shí)候才能知道的侥祭。比如說如果我們訂閱著一個(gè)輸入框的輸入內(nèi)容叁执,我們不可能事先就知道用戶會(huì)輸入什么茄厘,而是用戶每輸入一個(gè)字,這個(gè)輸入框關(guān)聯(lián)的Observable就會(huì)發(fā)出一個(gè)Event攜帶著這個(gè)用戶輸入的內(nèi)容徒恋,通知給所有訂閱者蚕断。

所以我們真正需要的應(yīng)該是一個(gè)類似即是可以發(fā)出Event的Observable,同樣也是可以接受新數(shù)據(jù)的訂閱者入挣。這個(gè)就是我們要說的Subjects亿乳,Subjects能夠動(dòng)態(tài)地接收新的值,這就相當(dāng)于它本身一定程度上是一個(gè)訂閱者径筏。當(dāng)它有了新的值之后就會(huì)通過Event將新值發(fā)出給他的所有訂閱者葛假,這樣看來它又是一個(gè)Observable了。

Subjects的分類

一共有四種Subjects滋恬,分別叫做PublishSubject聊训,BehaviorSubject,ReplaySubject和Variable恢氯。他們之間其實(shí)大致相同带斑,但是有具體自己鮮明的特點(diǎn),可以針對(duì)不同的使用場(chǎng)景進(jìn)行選擇勋拟。
可以說他們都是Observable勋磕,他們的訂閱者都能收到他們發(fā)出的新的Event,他們之間最大的區(qū)別只是在于一個(gè)新的訂閱者剛訂閱它的時(shí)刻能不能收到Subject以前發(fā)出過的舊Event敢靡,如果能的話那么收到多少個(gè)挂滓。

下面來一一地介紹


PublishSubjects

PublishSubject可以說是最普通的Subject,它不需要初始值就能創(chuàng)建啸胧。PublishSubject的訂閱者從他們開始訂閱的時(shí)間點(diǎn)起赶站,可以收到以后Subject發(fā)出的新Event,而不會(huì)收到他們訂閱前已發(fā)出的Event纺念。
直到Subject發(fā)出.complete或者.error的Event后贝椿,Subject則終結(jié)了,它也就不會(huì)再發(fā)出.next事件陷谱。但是對(duì)于在subject終結(jié)后再訂閱他的訂閱者团秽,也能收到subject發(fā)出的一條.complete或者.error的event,告訴這個(gè)新的訂閱者它已經(jīng)終結(jié)了叭首。
看一看下面這個(gè)時(shí)序圖就跟直觀的理解了习勤,最上面這條是一個(gè)PublishSubject,他在三個(gè)事件點(diǎn)上發(fā)出了三個(gè)Event焙格,第二條和第三條線是兩個(gè)新的訂閱图毕,虛線向上的箭頭表示他們訂閱PublishSubject的動(dòng)作,可以很清楚的看看PublishSubject的訂閱者只能收到他們訂閱后的Event眷唉。


PublishSubjects

接下來上代碼:

func publishSubject() {
  
  // 1
  let disposeBag = DisposeBag()
  
  // 2
  let subject = PublishSubject<String>()
  
  // 3
  subject.onNext("Hello_1")
  
  // 4
  subject.subscribe(onNext: { string in
    print("subscribe_1:", string)
  }, onCompleted:{
    print("subscribe_1: onCompleted")
  }).disposed(by: disposeBag)
  
  // 5
  subject.onNext("Hello_2")
  
  // 6
  subject.subscribe(onNext: { string in
    print("subscribe_2:", string)
  }, onCompleted:{
    print("subscribe_2: onCompleted")
  }).disposed(by: disposeBag)
  
  // 7
  subject.onNext("Hello_3")
  
  // 8
  subject.onCompleted()
  
  // 9
  subject.onNext("Hello_4")

  // 10
  subject.subscribe(onNext: { string in
    print("subscribe_3:", string)
  }, onCompleted:{
    print("subscribe_3: onCompleted")
  }).disposed(by: disposeBag)
}
publishSubject() print output

運(yùn)行一下我們的測(cè)試方法 publishSubject() 予颤,結(jié)合輸出結(jié)果來一步一步講解:
1.創(chuàng)建了一個(gè)DisposeBag
2.創(chuàng)建一個(gè)PublishSubject
3.對(duì)subject調(diào)用onNext方法囤官,這個(gè)方法相當(dāng)于subject接受到一個(gè).next事件,同時(shí)它會(huì)馬上把這個(gè)事件轉(zhuǎn)發(fā)給他當(dāng)前所有的訂閱者(所以說Subject即使Observable又是訂閱者)蛤虐。但是此時(shí)并沒有任何訂閱subject的行為党饮,所以你能看到上面并沒有打印出"Hello_1"這個(gè)信息。
4.我們訂閱subject驳庭,打印出.next的事件數(shù)據(jù)和.completed事件
5.再次調(diào)用subject的onNext方法刑顺,再次發(fā)出一句"Hello_2",可以看到打印"subscribe_1: Hello_2"這一句饲常,可見我們剛剛的訂閱有作用了
6.我們?cè)俅斡嗛唖ubject蹲堂,輸出用"subscribe_2"來區(qū)分標(biāo)示
7.再次調(diào)用subject的onNext方法,再次發(fā)出一句"Hello_3"贝淤。能看到兩次訂閱都打印出了"Hello_3"
8.對(duì)subject調(diào)用onCompleted方法柒竞,表示這個(gè)subject完成了,它以后再也不會(huì)發(fā)出.next事件了播聪⌒嗷可以看到subscribe_1和subscribe_2都打印出了.competed事件。
9.再次調(diào)用subject的onNext方法离陶,再次發(fā)出一句"Hello_4"稼虎,但是你看沒有任何"Hello_4"的打印,因?yàn)閟ubject已經(jīng)completed了枕磁,他已無法再發(fā)出.next事件了
10.我們?cè)俅斡嗛唖ubject渡蜻,可以看到立刻打印了"subscribe_3: onCompleted"术吝,這表示雖然第三次訂閱是發(fā)生在subject發(fā)出.completed之后计济,但是第三次訂閱也能收到subject的.completed事件,告訴你我已經(jīng)結(jié)束了排苍,別再瞎訂閱了B偌拧!淘衙!


BehaviorSubjects

BehaviorSubject需要通過一個(gè)默認(rèn)初始值來創(chuàng)建传藏,當(dāng)一個(gè)訂閱者來訂閱它的時(shí)候,這個(gè)訂閱者會(huì)立即收到BehaviorSubjects的上一個(gè)發(fā)出的event彤守,之后就跟正常的情況一樣毯侦,它也會(huì)接收到BehaviorSubjects之后發(fā)出的新的event。所以它必須有初始值具垫,不然當(dāng)它剛創(chuàng)建就有人來訂閱它的時(shí)候侈离,它就沒有"上一個(gè)值"能用來發(fā)出了。

BehaviorSubjects

再上代碼和輸出結(jié)果一起來講解:

func behaviorSubject() {
  
  // 1
  let disposeBag = DisposeBag()
  
  // 2
  let subject = BehaviorSubject(value: "A")

  // 3
  subject.subscribe { event in
      print("subscribe_1:", event)
  }.disposed(by: disposeBag)

  // 4
  subject.onNext("B")

  // 5
  subject.onError(NSError(domain: "xxx", code: 0, userInfo: nil))
  
  // 6
  subject.subscribe { event in
    print("subscribe_2:", event)
  }.disposed(by: disposeBag)

}
behaviorSubject() print output

記得調(diào)用一次測(cè)試方法behaviorSubject()筝蚕。
1.又是先創(chuàng)建一個(gè)DisposeBag
2.創(chuàng)建一個(gè)初始值為"A"的BehaviorSubject卦碾,初始值你也可以理解為這個(gè)Subject一創(chuàng)建就調(diào)用了一個(gè)onNext方法铺坞,發(fā)出了一個(gè)值為"A"的Event
3.訂閱一次Subject,可以看到打印出了這句"subscribe_1: next(A)"洲胖,雖然這個(gè)訂閱1晚于Subject"發(fā)出"A事件济榨。但是他也能收到Subject上一個(gè)發(fā)出的值(這個(gè)例子里是它的創(chuàng)建默認(rèn)值)。
4.調(diào)用subject的onNext方法再次發(fā)出一個(gè)含有"B"的.next事件绿映,可以看到subscribe_1也正常的打印出了
5.調(diào)用subject的onError方法擒滑,表示subject遇到一個(gè)錯(cuò)誤,并且終結(jié)了绘梦,以后也不會(huì)發(fā)出.next事件了橘忱。可以看到subscribe_1也正常打印出了這個(gè)錯(cuò)誤事件
6.再次訂閱這個(gè)已經(jīng)終結(jié)的subject卸奉,可以看到subscribe_2頁打印出了Subject的錯(cuò)誤钝诚,但未打印出任何.next事件值了。


ReplaySubject

ReplaySubject的關(guān)鍵就在于它在創(chuàng)建時(shí)候要給它設(shè)置一個(gè)bufferSize榄棵,這個(gè)bufferSize就表示它對(duì)于它發(fā)出過的event進(jìn)行緩存的一個(gè)數(shù)量大小凝颇。

比如一個(gè)ReplaySubjects的bufferSize設(shè)置為2,它發(fā)出了3個(gè).next的event疹鳄,那么它會(huì)將后兩個(gè)(比較新的)發(fā)出的event給緩存起來拧略,此時(shí)如果有一個(gè)subscriber訂閱了這個(gè)ReplaySubject,那么這個(gè)subscriber就會(huì)立即收到前面緩存的兩個(gè).next的event瘪弓。
(所以上面說過的BehaviorSubject有點(diǎn)類似于bufferSize為1的ReplaySubject)

如果在訂閱之前垫蛆,ReplaySubject就發(fā)出了.error或者.complete的event,那么此時(shí)如果一個(gè)subscriber訂閱已經(jīng)結(jié)束的ReplaySubject腺怯,除了會(huì)收到緩存的.next的event還會(huì)收到那個(gè)終結(jié)的.error或者.complete的event袱饭。
(這點(diǎn)是跟BehaviorSubject不同的,BehaviorSubject在終結(jié)后如果再訂閱只能收到終結(jié)的.error或者.complete呛占,而不能收到上一個(gè)發(fā)出的.next了)

所以這個(gè)bufferSize的大小很關(guān)鍵虑乖,如果設(shè)置不當(dāng)很容易對(duì)內(nèi)存消耗產(chǎn)生很大的壓力。


ReplaySubjects

上代碼示例:


func replaySubject() {
  // 1
  let disposeBag = DisposeBag()
  
  // 2
  let subject = ReplaySubject<String>.create(bufferSize: 2)
  
  // 3
  subject.onNext("A")
  subject.onNext("B")
  subject.onNext("C")
  
  // 4
  subject.subscribe { event in
      print("subscribe_1:", event)
  }.disposed(by: disposeBag)
  
  // 5
  subject.onNext("D")
  
  // 6
  subject.subscribe { event in
    print("subscribe_2:", event)
  }.disposed(by: disposeBag)
  
  // 7
  subject.onCompleted()
  
  // 8
  subject.subscribe { event in
    print("subscribe_3:", event)
  }.disposed(by: disposeBag)
}
replaySubject() print output

還是記得調(diào)用一次上面剛寫的測(cè)試方法replaySubject()晾虑。
1.照舊創(chuàng)建一個(gè)DisposeBag
2.創(chuàng)建一個(gè)ReplaySubject疹味,bufferSize設(shè)置為2,表示他會(huì)緩存2個(gè)發(fā)出的.next事件
3.我們調(diào)用3次subject的onNext方法帜篇,發(fā)出"A"糙捺、"B"、"C"三個(gè)值笙隙,因?yàn)槲覀冊(cè)O(shè)置了bufferSize為2洪灯,以后最后兩個(gè)event會(huì)被緩存下來
4.開始第一次的訂閱,看打印是不是輸出了"subscribe_1: next(B)"和"subscribe_1: next(C)"逃沿。這就表示這次訂閱剛發(fā)生的時(shí)候就會(huì)收到subject緩存的已發(fā)出過的event
5.再次調(diào)用subject的onNext方法發(fā)出一個(gè)"D"婴渡,看打印subscribe_1也正常的接收到了這個(gè)時(shí)間幻锁。注意這個(gè)時(shí)候緩存的兩個(gè)值就從B和C變?yōu)榱薈和D
6.第二次訂閱subject看輸出確實(shí)是"subscribe_2: next(C)"和"subscribe_2: next(D)",這就驗(yàn)證了subject的緩存內(nèi)容確實(shí)在不斷更新
7.調(diào)用subject的onCompleted方法边臼,終結(jié)了這個(gè)subject哄尔。可以看到subscribe_1和subscribe_2都收到了這個(gè)完成的事件并且打印了出來
8.第三次訂閱柠并,因?yàn)檫@個(gè)subject已經(jīng)終結(jié)了岭接,所以可以看到subscribe_3除了接收并輸出了緩存的兩個(gè)事件以外還接收到了completed事件。這里需要注意的就是臼予,跟前面的subject不同鸣戴,即使subject終結(jié)了,后來的訂閱者也還是能收到已緩存的.next事件粘拾。


Variables

最后一類叫Variables窄锅,它其實(shí)是封裝了一個(gè)BehaviorSubjects,它除了具備BehaviorSubjects的功能缰雇,能夠向它的訂閱者發(fā)出上一個(gè)的event和之后新創(chuàng)建的event之外入偷,同時(shí)還把會(huì)把當(dāng)前發(fā)出的值保存為自己的狀態(tài),正因?yàn)樗菍?duì)BehaviorSubjects的封裝械哟,所以它也必須要通過一個(gè)默認(rèn)的初始值進(jìn)行創(chuàng)建疏之。
通俗點(diǎn)將就是Variables有一個(gè)value屬性,你通過改變這個(gè)value屬性的值就相當(dāng)于對(duì)一般的Subjects調(diào)用onNext()方法暇咆,而這個(gè)最新的onNext()的值就被一只保存在value屬性里了锋爪,直到你再次修改它。
但是Variables的value不能發(fā)出.error和.complete的event爸业,即你不能給value賦值.error或者.complete其骄,當(dāng)Variables要deallocate的時(shí)候他會(huì)自動(dòng)的complete。
Variables本身沒有subscribe()方法沃呢,但是所有Subjects都有一個(gè)asObservable()方法年栓,這個(gè)方法就會(huì)返回這個(gè)Subject的Observable類型拆挥,拿到這個(gè)Observable類型我們就能訂閱它了薄霜。
Variables之所以叫做Variables,就是在于它可以適用于多變的使用場(chǎng)景纸兔。比如你可以把它當(dāng)做一般的Subjects來使用惰瓜,需要訂閱然后接受.next的event。你也可以在你需要查看當(dāng)前值的時(shí)候汉矿,直接去訪問Variables的value屬性崎坊,而不用去訂閱它和不斷的一直接收新event。

上代碼:

func variable() {
  // 1
  let disposeBag = DisposeBag()
  
  // 2
  var variable = Variable("A")
  
  // 3
  variable.value = "B"
  
  // 4
  variable.asObservable().subscribe {
    print("subscribe_1:", $0)
  }.disposed(by: disposeBag)

  // 5
  variable.value = "C"
  
  // 6
  variable.asObservable().subscribe {
    print("subscribe_2:", $0)
  }.disposed(by: disposeBag)
  
  // 7
  variable.value = "D"

}
variable() print output

調(diào)用測(cè)試方法 variable() 然后看看輸出結(jié)果洲拇,我們來一步一步講解:
1.創(chuàng)建一個(gè)DisposeBag
2.因?yàn)閂ariables它是對(duì)BehaviorSubjects的封裝奈揍,所以它也需要默認(rèn)初始值來創(chuàng)建曲尸,這里我們用了"A"
3.我們修改variable.value 為"B",這就相當(dāng)于對(duì)Subject調(diào)用了onNext("B")方法男翰,此時(shí)如果有已訂閱者就可以收到.next的事件
4.通過asObservable()方法或者內(nèi)部的那個(gè)Subject另患,然后訂閱它,因?yàn)樗哂懈鶥ehaviorSubjects同樣的特性蛾绎,所以當(dāng)一訂閱它的時(shí)候昆箕,立刻就會(huì)收到一條上一條已發(fā)出的.next事件∽夤冢可以看到輸出打印了"subscribe_1 :next(B)"
5.再次修改value值為C鹏倘,可以看到打印"subscribe_1 :next(C)",表明訂閱1收到了新的這個(gè).next事件
6.再次進(jìn)行一次訂閱顽爹,同理會(huì)立刻收到上一條發(fā)出的.next纤泵,可見打印輸出了"subscribe_2: next(C)"
7.最后一次再修改value值為D,結(jié)果當(dāng)然就是subscribe_1和subscribe_2都收到了.next(D)的事件并且打印了出來
8.最后重點(diǎn)看最后兩個(gè)輸出結(jié)果镜粤,subscribe_1和subscribe_2還接收到了completed事件夕吻,這是為什么呢?因?yàn)榫拖裆厦娼榻B里提到的繁仁,你不能手動(dòng)給Variables發(fā)送completed或者error事件來結(jié)束它涉馅,而是當(dāng)Variables要自己deallocate的時(shí)候他會(huì)自動(dòng)的發(fā)出complete。因?yàn)槲覀冞@個(gè)Variable對(duì)象初始化在func variable()方法內(nèi)黄虱,所以他的生命周期就被限制在了方法內(nèi)稚矿,當(dāng)這個(gè)方法全部執(zhí)行完畢的時(shí)候,這個(gè)Variable對(duì)象就要被銷毀了捻浦,所以它也就自動(dòng)地向它的所有訂閱者發(fā)出了completed事件晤揣。




至此RxSwift的最基本的一些概念就介紹完了,之后有時(shí)間時(shí)候會(huì)繼續(xù)漸進(jìn)的寫文章介紹RxSwift或者RxCocoa朱灿。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末昧识,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子盗扒,更是在濱河造成了極大的恐慌跪楞,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侣灶,死亡現(xiàn)場(chǎng)離奇詭異甸祭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)褥影,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門沾谓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吹埠,“玉大人把还,你說我怎么就攤上這事∩薅叮” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵寨典,是天一觀的道長(zhǎng)熏迹。 經(jīng)常有香客問我,道長(zhǎng)凝赛,這世上最難降的妖魔是什么注暗? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮墓猎,結(jié)果婚禮上捆昏,老公的妹妹穿的比我還像新娘。我一直安慰自己毙沾,他們只是感情好骗卜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著左胞,像睡著了一般寇仓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上烤宙,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天遍烦,我揣著相機(jī)與錄音,去河邊找鬼躺枕。 笑死服猪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拐云。 我是一名探鬼主播罢猪,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼叉瘩!你這毒婦竟也來了膳帕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤薇缅,失蹤者是張志新(化名)和其女友劉穎危彩,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捅暴,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恬砂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年咧纠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蓬痒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡漆羔,死狀恐怖梧奢,靈堂內(nèi)的尸體忽然破棺而出狱掂,到底是詐尸還是另有隱情,我是刑警寧澤亲轨,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布趋惨,位于F島的核電站,受9級(jí)特大地震影響惦蚊,放射性物質(zhì)發(fā)生泄漏器虾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一蹦锋、第九天 我趴在偏房一處隱蔽的房頂上張望兆沙。 院中可真熱鬧,春花似錦莉掂、人聲如沸葛圃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽库正。三九已至,卻和暖如春厘唾,著一層夾襖步出監(jiān)牢的瞬間褥符,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工抚垃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留属瓣,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓讯柔,卻偏偏與公主長(zhǎng)得像抡蛙,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子魂迄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 RxSwift入坑解讀-你所需要知道的各種概念 沸沸騰關(guān)注 2016.11.27 19:11*字...
    楓葉1234閱讀 2,797評(píng)論 0 2
  • 最近在學(xué)習(xí)RxSwift相關(guān)的內(nèi)容粗截,在這里記錄一些基本的知識(shí)點(diǎn),以便今后查閱捣炬。 Observable 在RxSwi...
    L_Zephyr閱讀 1,756評(píng)論 1 4
  • 首先,何為RxSwift? RxSwift是ReactiveX的Swift版本,一個(gè)響應(yīng)式變成框架熊昌。傳送門 開始之...
    cocoawork丶閱讀 468評(píng)論 0 3
  • 今天4月13日推溃,人間最美四月天中這樣尋常的一天昂利。昨日還是春風(fēng)和煦,溫潤如玉。今日早上起來蜂奸,外面卻沒來由地飄起小雨犁苏,...
    漣漪淺綠閱讀 599評(píng)論 6 7
  • 《時(shí)間管理》課后心得 時(shí)間都去哪兒了,還沒好好感受年輕就老了扩所,時(shí)間都去哪了围详,還沒好好看看你眼睛就花了;多么感人的歌...
    瑞成大哥閱讀 459評(píng)論 2 4