RxSwift的學(xué)習(xí)之路(一)——Observable

前言

最近在學(xué)習(xí)swift眉厨,看著喵神翻譯的《Swift進(jìn)階》滑黔,一直看著頗感疲乏僻他,于是想邊學(xué)邊實踐來讓自己不那么的犯困宵距。畢竟紙上得來終覺淺嘛所以我就順手入了Raywenderlich上的RxSwift的書籍(這是一本好書,延續(xù)了Raywenderlich一貫的風(fēng)格——講課與動手相結(jié)合吨拗,每一章都有一個樣例程序讓你一步步跟著去實現(xiàn)满哪,結(jié)尾還會有challenge)。并且丢胚,這也是我在簡書的第一篇文章翩瓜,主要是我的手提上的hexo掛了,掛在github上的博客也沒法更新了携龟,弄了兩個星期都沒弄好兔跌,沒轍了。然而學(xué)習(xí)仍在繼續(xù)峡蟋,筆記也是要做的坟桅,所以就先來簡書寫寫吧

然后圍繞著RxSwift這個主題华望,估計我會寫上好幾篇文章,基本上每個小的主題也都會跟著《RxSwift--Reactive Programming with Swift》這本書來仅乓,你也會看到很多這本書的影子赖舟。那么如果你沒有這本書,通過我的文章來一窺RxSwift的究竟我想也是可以的夸楣,假如你有買這本書宾抓,在學(xué)習(xí)的路上有什么不懂的可以留言評論互相交流,如果你看到我的博客有什么不對的地方也希望指出改正豫喧!提前謝過石洗!

RxSwift的書

RxSwift簡介

我對RxSwift的了解并不多,但就依我目前的了解紧显,RxSwift主要是提供了一種在Swift語言下可以輕易寫出響應(yīng)式函數(shù)式的功能實現(xiàn)讲衫。主要就是方便了我們現(xiàn)在的多線程處理獲取數(shù)據(jù)并進(jìn)行處理的這么一個流程。說到后臺獲取數(shù)據(jù)孵班,你的第一反應(yīng)大概是GCD, OperationQueue, Delegate, NotificationCenter. 為此你不得不實現(xiàn)一些協(xié)議涉兽,寫多幾個方法,甚至要實現(xiàn)一些類篙程。然而RxSwift和RxCocoa的出現(xiàn)讓我們擺脫了這些枷畏。讓你可以在無數(shù)的.function()調(diào)用函數(shù)中立即做出對即將獲取到的數(shù)據(jù)的處理。而且它最基礎(chǔ)的實現(xiàn)房午,源自于Observable矿辽,因為Observable就像一條數(shù)據(jù)流,當(dāng)新的數(shù)據(jù)出現(xiàn)郭厌,它就會通知它的Subscriber去做對應(yīng)的處理袋倔。總而言之折柠,比起曾經(jīng)你在Cocoa Touch上所用到的方式宾娜,使用RxSwift后,你會看到一片不一樣的天地扇售。

RxSwift安裝指南

詳見官網(wǎng)

什么是Observable前塔?

Observable我也不知道中文怎么翻譯,直譯一下就是可觀察的承冰,但它通常都會被實例化华弓,于是我稱它為可觀察的實例。
實質(zhì)上困乒,它就是一個Sequence(序列)寂屏,序列分為有窮序列和無窮序列,主要就是用來形成一條數(shù)據(jù)流。說到有窮的數(shù)據(jù)流迁霎,我們不難想到通過網(wǎng)絡(luò)來獲取一張圖片這一個過程吱抚,數(shù)據(jù)是有限的,圖片下載完了考廉,序列也就迎來閉合了秘豹;而說到無窮序列,一般就是我們對UI的監(jiān)測可能是無窮的昌粤,比如某個手勢既绕、某次按鈕的點擊、橫豎屏切換等等婚苹。

而你所有的操作產(chǎn)生的數(shù)據(jù)都會通過Observable傳輸——所以實質(zhì)上Observable就是一條數(shù)據(jù)流岸更。

Observable的實例示意圖

Observable的三種事件

Observable里有三種事件——next, completed, error

  • next事件主要是當(dāng)Observable里出現(xiàn)新的數(shù)據(jù)時會發(fā)出的事件,同時該事件會攜帶新的數(shù)據(jù)對象膊升。
  • completed事件是當(dāng)Observable不再有新的數(shù)據(jù)出現(xiàn),Observable被標(biāo)記完成谭企,并且將數(shù)據(jù)流終結(jié)(terminate)廓译。
  • error事件是指當(dāng)數(shù)據(jù)流遇到了錯誤會發(fā)出的事件,該事件也會導(dǎo)致Observable被終結(jié)债查。
    值得一提的是非区,被終結(jié)的Observable不會再有任何的事件發(fā)出。

Observable如何創(chuàng)建盹廷?

just, of, from

Observable類自帶三個方法來創(chuàng)建一個Observable實例征绸,分別使用于不同的情況。of方法毫無疑問是最常用也最方便的一個方法俄占。但是現(xiàn)在還是把每一個都做一個簡單的介紹吧管怠!

just方法所創(chuàng)建出的Observable實例可以說是和just的字面意義非常的相近了,因為just僅能傳入一個參數(shù)作為Observable內(nèi)所包含的元素來創(chuàng)建一個實例缸榄〔吵冢考慮以下代碼片段:

let one = 1
let two = 2

let variable = Observable.just(one)

這就是用just來創(chuàng)建一個Observable實例的方式。如果你也run了上面的代碼甚带,你可以用option+鼠標(biāo)左鍵點擊上面的變量variable她肯,你可以發(fā)現(xiàn)編譯器的類型推斷為Observable<Int>。是的鹰贵,just, of方法接受的參數(shù)都是泛型的晴氨。

of方法可以接受多個參數(shù)來創(chuàng)建實例〉锸洌考慮以下代碼片段:

let one = 1
let two = 2
let three = 3

let variable = Observable.of(one, two , three)
let variable2 = Observable.of([one, two, three])

回顧一下我之前說的籽前,Observable實質(zhì)上就是一條數(shù)據(jù)流,所有的創(chuàng)建方法所傳入的參數(shù)都是這條數(shù)據(jù)流上的元素。上面的那段代碼聚假,對于variable變量而言块蚌,one, two, three就是里面的元素,對于variable2而言膘格,數(shù)組[one, two, three]就是里面的元素峭范。option+click一下,variable被推斷成為Observable<Int>瘪贱,variable2被推斷成為Observable<[Int]>纱控。這里為什么要看似多此一舉的聲明一個variable2呢?因為這和接下來的from方法有關(guān)系菜秦,為了理清楚兩個創(chuàng)建方法的不同甜害,就在這里先聲明一個這樣的變量先。

from方法只接收數(shù)組作為參數(shù)球昨,并抽取出數(shù)組里的元素來作為數(shù)據(jù)流中的元素尔店。代碼如下:

let variable = Observable.from([1, 2, 3])

在這里,如果你用option+click去看variable的推斷類型的話主慰,你可以發(fā)現(xiàn)它的推斷類型是Observable<Int>嚣州,和of不同的是,of創(chuàng)建的實例以數(shù)組作為元素共螺;from則以1, 2, 3作為元素该肴。

range方法

利用range我們可以快速的創(chuàng)建一個包含一段范圍內(nèi)的正整數(shù)的數(shù)據(jù)流。它接受startcount作為參數(shù)來表示一段創(chuàng)建的范圍藐不。

let observable = Observable<Int>.range(start: 1, count: 10)

empty()——創(chuàng)建一個空的可觀察的實例

為了讓接下來的創(chuàng)建方法更加地容易理解匀哄,我不得不把稍后才會講到訂閱(subscribe)方法提前使用,但是我相信你能看懂雏蛮,至于詳盡的使用方式涎嚼,就見下文吧!
empty()方法主要是創(chuàng)建了一個空的數(shù)據(jù)流底扳,里面沒有任何的元素铸抑,所以它不會發(fā)出任何的next事件,它只會發(fā)出一個completed事件衷模,然后終結(jié)鹊汛。還有一點是,沒有被訂閱的Observable實例并不會發(fā)出任何事件阱冶,也不會終結(jié)刁憋,所以你看到上面所創(chuàng)建的數(shù)據(jù)流,其中的數(shù)據(jù)也并沒有被處理木蹬。
直到訂閱者出現(xiàn)至耻,數(shù)據(jù)流就會開始向訂閱者發(fā)送事件了!

let observable = Observable<Void>.empty()

observable
  .subscribe(
    onNext: {element in
      print(element)
    }, 
    onCompleted: {
      print("completed")
    })
/*
輸出:
completed
*/

這段代碼首先用empty()創(chuàng)建了一個不含元素的Observable<Void>實例,然后訂閱了這個實例的next事件和completed事件尘颓,由于這條數(shù)據(jù)流不含任何元素走触,所以它直接向訂閱者發(fā)送了一個completed事件然后就終結(jié)了。

never()——創(chuàng)建一個代表無盡時長的數(shù)據(jù)流

never()方法創(chuàng)建了一個不會發(fā)出任何事件也不會終結(jié)的Observable實例疤苹,盡管如此互广,它也可以代表一段無限(相對無限)的時長。但是卧土,如果它不能發(fā)出任何事件惫皱,那又有什么用呢?其實它可以配合do方法來做一些工作尤莺,它還可以被subscribedispose“事件”旅敷,來判斷在當(dāng)前的環(huán)境下,never()是什么時候被回收的颤霎。當(dāng)然媳谁,disposedo目前都沒有介紹,詳情見下文友酱。

Subscribe

訂閱一個Observable實例主要有兩個方法韩脑,talk is cheap, show you the code.

let observable = Observable.of(1, 2, 3)

//1
observable.subscribe({ event in
  print(event)
})

//2
observable.subscribe(onNext: { element in
  print(element)
})

/*
輸出結(jié)果:
next(1)
next(2)
next(3)
completed
1
2
3
*/

這段代碼里用了兩個方法來subscribe一個Observable對象,方法1直接subscribe了所有的事件粹污,在回調(diào)的block里獲取到的參數(shù)就是事件本身;而方法2則提供了幾個可選的參數(shù)比如onNext, onError, onCompleted, onDisposed來訂閱到不同的事件首量,而我提供的樣例里訂閱的是next事件壮吩。next事件的回調(diào)block會傳入當(dāng)前next事件所攜帶的元素。

dispose和DisposeBag——讓訂閱者釋放

如果你足夠細(xì)心的話加缘,你會發(fā)現(xiàn)每次subscribe方法調(diào)用后都會返回一個Disposable對象鸭叙,代表著每一次訂閱都是需要被釋放的,遺憾的是拣宏,這并不由ARC進(jìn)行管理沈贝,而是由RxSwift來管理。然而勋乾,RxSwift也沒有提供自動釋放的機(jī)制宋下,始終是需要你手動釋放的。

一種方法是調(diào)用Disposabledispose()方法辑莫。

let observable = Observable.of("A", "B", "C")

observable
    .subscribe(onNext: { element in
        print(element)
    }, onCompleted: { 
        print("completed")
    }, onDisposed: { 
        print("disposed")
    })
    .dispose()

/*
輸出結(jié)果:
A
B
C
completed
disposed
*/

每次訂閱都要單獨管理它的釋放是非常冗余的(書里原話学歧,但實質(zhì)上我也沒感覺和加到垃圾袋里有多大差別,反正也是每次要多調(diào)用一個函數(shù))各吨,所以RxSwift提供了一個類似于Auto Release Pool的釋放機(jī)制枝笨,稱為DisposeBag。被加到DisposeBagDisposable對象會在DisposeBag將要釋放的時候被逐一調(diào)用dispose()。它的使用是這樣的:

let disposeBag = DisposeBag()
    
Observable.of("A", "B", "C")
    .subscribe { print($0) }
    .addDisposableTo(disposeBag)

/*
輸出結(jié)果:
next(A)
next(B)
next(C)
completed

實際上横浑,從上面兩段的代碼我們可以看出來剔桨,雖然subscribe可以訂閱到一次訂閱被釋放時的disposed"事件",是的徙融,我這里打了雙引號洒缀,因為它和next, error, completed不一樣。從第二段代碼來看就很明了了张咳,并沒有打印出disposed相關(guān)的事件帝洪。

create——創(chuàng)建一條可自定義事件的數(shù)據(jù)流

create嘛,功能就像標(biāo)題說的那樣脚猾,創(chuàng)建一個Observable對象葱峡,但是里面的事件可以有你來自定義。

enum MyError: Error {
    case anError
}

let disposeBag = DisposeBag()

Observable<String>.create { observer in
    observer.onNext("1")
    
    observer.onError(MyError.anError)
    
    observer.onCompleted()
    
    observer.onNext("?")
    
    return Disposables.create() //return a disposable to represent a subscription
}
.subscribe(
    onNext: { print($0) },
    onError: { print($0) },
    onCompleted: { print("completed") },
    onDisposed: { print("Disposed") }
)
.addDisposableTo(disposeBag)

/*
輸出結(jié)果:
1
anError
Disposed
*/

正如代碼所示龙助,上面的create為一個observer添加了next, error, completed, next事件砰奕,但是很顯然它輸出到error事件就終結(jié)了,后面的事件也不會再發(fā)出提鸟。
如果你想的話军援,你可以標(biāo)注或者移位上面的代碼,來看看不同的輸出称勋。

deferred——創(chuàng)建一個可以生成不同的Observable對象的工廠

deferred就是一個Observable的工廠胸哥,你可以在里面定制幾條不同的數(shù)據(jù)流,從而提供給外部不同的訂閱赡鲜,代碼如下:

let disposeBag = DisposeBag()

var flip = false

let factory: Observable<Int> = Observable.deferred {
    flip = !flip
    
    if flip {
        return Observable.of(1, 2, 3)
    }
    else {
        return Observable.of(4, 5, 6)
    }
}

for _ in 0...3 {
    factory.subscribe(onNext: { print($0, terminator:"") })
    .addDisposableTo(disposeBag)
    
    print()
}

/*
輸出結(jié)果:
123
456
123
456
*/

上面的代碼就是空厌,我在一個deferred工廠里定義了兩條不同的數(shù)據(jù)流,分別是1, 2, 34, 5, 6银酬,設(shè)置了一個翻轉(zhuǎn)變量flip嘲更,隨著每次訂閱,奇數(shù)次的訂閱訂閱到的是1, 2, 3數(shù)據(jù)流揩瞪,而4, 5, 6數(shù)據(jù)流則是由偶數(shù)次的訂閱訂閱到赋朦。
好了,這就是本章節(jié)的全部內(nèi)容了李破。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宠哄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子喷屋,更是在濱河造成了極大的恐慌琳拨,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屯曹,死亡現(xiàn)場離奇詭異狱庇,居然都是意外死亡惊畏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門密任,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颜启,“玉大人,你說我怎么就攤上這事浪讳$终担” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵淹遵,是天一觀的道長口猜。 經(jīng)常有香客問我,道長透揣,這世上最難降的妖魔是什么济炎? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮辐真,結(jié)果婚禮上须尚,老公的妹妹穿的比我還像新娘。我一直安慰自己侍咱,他們只是感情好耐床,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著楔脯,像睡著了一般撩轰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上昧廷,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天钧敞,我揣著相機(jī)與錄音,去河邊找鬼麸粮。 笑死,一個胖子當(dāng)著我的面吹牛镜廉,可吹牛的內(nèi)容都是我干的弄诲。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼娇唯,長吁一口氣:“原來是場噩夢啊……” “哼齐遵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起塔插,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤梗摇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后想许,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伶授,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡断序,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了糜烹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片违诗。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖疮蹦,靈堂內(nèi)的尸體忽然破棺而出诸迟,到底是詐尸還是另有隱情,我是刑警寧澤愕乎,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布阵苇,位于F島的核電站,受9級特大地震影響感论,放射性物質(zhì)發(fā)生泄漏绅项。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一笛粘、第九天 我趴在偏房一處隱蔽的房頂上張望趁怔。 院中可真熱鬧,春花似錦薪前、人聲如沸润努。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铺浇。三九已至,卻和暖如春垛膝,著一層夾襖步出監(jiān)牢的瞬間鳍侣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工吼拥, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留倚聚,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓凿可,卻偏偏與公主長得像惑折,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子枯跑,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350

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