本文章內(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自己的定義是這樣寫的:
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就相當(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棒卷。
上面兩個(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>
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>
訂閱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é)果是什么:
可以看到我們?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é)果
可以看到這個(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é)果:
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é)果是什么:
可以看到我們雖然兩次都是訂閱了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眷唉。
接下來上代碼:
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)
}
運(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ā)出了。
再上代碼和輸出結(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)
}
記得調(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)生很大的壓力。
上代碼示例:
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)
}
還是記得調(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"
}
調(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事件晤揣。