RxSwift學(xué)習(xí)之旅-初見RxSwift

概念性的東西就不在這里做過多的陳述了,在這里只說明兩點:

RxSwift究竟是什么

RxSwift is a library for composing asynchronous and event-based code by using observable sequences and functional style operators, allowing for parameterized execution via schedulers.

RxSwift幫我們解決了什么問題

  1. State, and specifically, shared mutable state
  2. Imperative programming
  3. Side effects
  4. Declarative code
  5. Reactive systems

這里只是很簡單的介紹了下RxSwift勾拉,別問為什么是英文捷凄,本人英語水平實在是不敢在這里班門弄斧,同時也避免翻譯的不準(zhǔn)確給你造成誤導(dǎo)浑玛。你可以在RxSwift的GitHub上找到更多關(guān)于它的詳細(xì)介紹

要使用RxSwift首先需要了解三樣?xùn)|西

Observables(被觀察者)

Observable<T>這個類提供了Rx的基礎(chǔ)代碼憎亚,它能夠產(chǎn)生一個異步的事件流去傳輸關(guān)于泛型T類型的相關(guān)數(shù)據(jù)衙耕。簡單點說就是它能夠在當(dāng)前類訂閱(接收到)另外一個類發(fā)送數(shù)據(jù)
Observable<T>這個類也允許多個observers響應(yīng)事件去更新UI或者去處理接收到的數(shù)據(jù)
ObservableType這是Observable<T>遵守的一個協(xié)議刃泌,也就限制了一個Observable只能發(fā)出(或者一個observers只能接收)三種類型的事件

  • A next event: 這個事件攜帶最新的數(shù)據(jù)凡壤,observers通過訂閱這個事件接收到最新的數(shù)據(jù)
  • A completed event: 這個事件表示一個事件序列的完結(jié),observers接收到這個事件后就再也接收不到其它任何事件
  • An error event: 一個事件序列因一個錯誤而終止耙替,observers接收到這個事件后就再也接收不到其它任何事件

Operators

類似于數(shù)學(xué)中的+ - * /亚侠,Rx也提供了一些方便我們對數(shù)據(jù)進(jìn)行處理的操作符

UIDevice.rx.orientation
  .filter { value in
    return value != .landscape
  }.map { _ in
    return "Portrait is the best!"
  }.subscribe(onNext: { string in
    showAlert(text: string)
  })

上面的例子中,每當(dāng)設(shè)備的方向發(fā)生改變俗扇,我們接收到事件硝烂,但是接收到的值并不是我們想要的最終結(jié)果,那我們就需要借助Rx中的操作符對數(shù)據(jù)進(jìn)行處理

Paste_Image.png
  1. 首先,filter只會允許.landscape通過值狐援。如果設(shè)備在橫向模式,訂閱代碼不會執(zhí)行,因為filter會抑制這些事件
  2. 在value = .portrait的情況下map接收到輸入钢坦,并把值轉(zhuǎn)換為一個字符串輸出的文本"Portrait is the best!"
  3. 最后你訂閱next事件究孕,在value = .portrait的情況下將接收到已經(jīng)處理完成的文本"Portrait is the best!"啥酱,你可以調(diào)用一個方法將這個文本直接顯示在屏幕上

Schedulers

Schedulers在Rx中等價于iOS當(dāng)中的dispatch queues爹凹,在Rx中已經(jīng)幫我們定義好了許多常用Schedulers,這些定義好的Schedulers可以幫助我們解決開發(fā)中的大部分問題

  • ConcurrentDispatchQueueScheduler可以并發(fā)執(zhí)行你的代碼
  • OperationQueueScheduler可以讓你在你給定的隊列上運行你的事件序列

在學(xué)習(xí)Rx基礎(chǔ)階段并不會使用到它镶殷,這里只是簡單的介紹禾酱,如果這系列文章能持續(xù)更新的話我打算后面再結(jié)合具體事例進(jìn)行更加詳細(xì)的講解

Getting started

了解完過后就可以學(xué)習(xí)一些Rx的基礎(chǔ)代碼了,首先創(chuàng)建一個工程绘趋,并使用CocoaPods將RxSwift集成到你的項目中颤陶,創(chuàng)建一個playground在你的項目中,以便更流暢的書寫Rx測試代碼陷遮,避免過多的command+R浪費很多時間滓走,完成后應(yīng)該是這樣的

Snip20170524_2.png

然后定義這樣一個方法,以便我們進(jìn)行更好的練習(xí)

public func example(of description: String, action: () -> Void) {
    print("\n--- Example of:", description, "---")
    action()
}
create

首先我們需要知道What is an observable?
常見的兩種方式來創(chuàng)建一個Observable對象帽馋,一種是通過引入RxCocoa(RxCocoa是對cocoa進(jìn)行的Rx擴(kuò)展)搅方,它已經(jīng)包含了我們常用到的Observable流,比如button的tap事件
let observable = loginButton.rx.tap.asObservable()
也可以使用提供的create函數(shù)來創(chuàng)建一個Observable對象

example(of: "create") {
    enum MyError: Error {
        case anError
    }
    
    Observable<String>.create({ (observer) -> Disposable in
        observer.onNext("1")
        observer.onNext("?")
        observer.onCompleted()
//        observer.onError(MyError.anError)
        return Disposables.create()
    }).subscribe({ (event) in
        print(event)
    }).disposed(by: DisposeBag())
}
//--- Example of: create ---
//next(1)
//next(?)
//completed

Disposing

到這里我們必須要知道dispose绽族,在一個observable被訂閱前它不做任何事情姨涡,subscribe觸發(fā)observable發(fā)送事件直到observable發(fā)送.completed.error事件終止,如果我們的subscribe沒有收到.completed.error事件那誰來終止這次observable sequence呢吧慢?那就是DisposeBag涛漂。Rx官方建議我們每一個subscribe都應(yīng)該dispose,以免造成內(nèi)存泄漏

example(of: "DisposeBag") {
    let disposeBag = DisposeBag()
    Observable.of("A", "B", "C").subscribe({
        print($0)
    }).addDisposableTo(disposeBag) 
}
![Uploading Snip20170524_14_517335.png . . .]

上述事例當(dāng)disposeBag生命周期結(jié)束時检诗,即使subscribe沒有收到.completed.error事件匈仗,observable sequence也將會被終止

just
example(of: "What is an observable?") { 
    let one = 1
    let observable: Observable<Int> = Observable<Int>.just(one)
}
Snip20170524_3.png

上面的事例中我們通過Observable.just方法創(chuàng)建了只包含一個元素的observable sequence,我們可以通過訂閱它獲取到它發(fā)送的事件

subscribe
example(of: "What is an observablee?") { 
    let one = 1
    let observable: Observable<Int> = Observable<Int>.just(one)

    observable.subscribe({ (event) in
        print(event)
    })
}
//print:
//--- Example of: What is an observable? ---
//next(1)
//completed
Snip20170524_6.png

這個方法可以訂閱到observable發(fā)出的任何事件逢慌,在上面的例子中我們看到observable發(fā)出了兩個事件next(1)completed
還有一個跟它很像的訂閱方法悠轩,也是我們開發(fā)中經(jīng)常用到的訂閱方法

Snip20170524_12.png

一目了然,對應(yīng)的事件在對應(yīng)的閉包當(dāng)中響應(yīng)涕癣,需要注意的是這里的參數(shù)就不在是事件了哗蜈,而是事件攜帶的具體的值。并且對于我們不關(guān)注的事件是可以省略的哦~

observable.subscribe(onNext: { (Int) in
        code
    }, onError: { (Error) in
        code
    }, onCompleted: { 
        code
    }, onDisposed: { 
        code
    })
of
example(of: "of") { 
    let one = 1
    let two = 2
    let three = 3
    let observable = Observable.of(one, two, three)
    
    observable.subscribe({ (event) in
        print(event)
    })
}
//print:
//--- Example of: of ---
//next(1)
//next(2)
//next(3)
//completed

Snip20170524_7.png

上面的事例中我們通過Observable.of方法創(chuàng)建了包含三個元素的observable squence坠韩,我們通過訂閱發(fā)現(xiàn)它使用三次next事件依次發(fā)送我們給它的三個值距潘,然后發(fā)送completed事件告訴我們這個observable的生命周期結(jié)束,不會再發(fā)送新的值只搁。如果我們將三個值包成一個數(shù)組會發(fā)生什么呢音比?

example(of: "of") {
    let one = 1
    let two = 2
    let three = 3
    let observable = Observable.of([one, two, three])
    
    observable.subscribe({ (event) in
        print(event)
    })
}
//print:
//--- Example of: of ---
//next([1, 2, 3])
//completed

他會將這個數(shù)組用一個next事件發(fā)送給我們,那如果我們開發(fā)中需要給他一個數(shù)組氢惋,讓他依次發(fā)送給我們應(yīng)該怎么辦呢洞翩?

from
example(of: "from") {
    let one = 1
    let two = 2
    let three = 3
    let observable = Observable.from([one, two, three])
    
    observable.subscribe({ (event) in
        print(event)
    })
}
//print:
//--- Example of: from ---
//next(1)
//next(2)
//next(3)
//completed
Snip20170524_8.png

改用from就搞定了稽犁,創(chuàng)建observable sequence的方法還有很多,直接上代碼吧

empty
example(of: "empty") { //只能收到completed
    let observable = Observable<Void>.empty()
    observable.subscribe({ (event) in
        print(event)
    })
}
//print:
//--- Example of: empty ---
//completed
Snip20170524_9.png
never
example(of: "never") { //不發(fā)送任何事件
    let observable = Observable<Any>.never()
    observable.subscribe({ (event) in
        print(event)
    })
}
//print:
Snip20170524_10.png
range
example(of: "range") { 
    let observable = Observable<Int>.range(start: 1, count: 10)
    observable.subscribe(onNext: { (i) in
        print(i)
    })
}
//print:
//--- Example of: range ---
//    1
//    2
//    3
//    4
//    5
//    6
//    7
//    8
//    9
//    10
Snip20170524_11.png
deferred
example(of: "deferred") { 
    let disposeBag = DisposeBag()
    var flip = false
    
    let factory:Observable<Int> = Observable.deferred({ () -> Observable<Int> in
        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()
    }
}
// --- Example of: deferred ---
//    123
//    456
//    123
//    456

Snip20170524_14.png

Subject

Subject就相當(dāng)于一個橋梁或者代理骚亿,它既可以作為一個observer也可以作為一個Observable已亥。

下面來看幾種不同的Subject:

PublishSubject

PublishSubject只會發(fā)送給訂閱者訂閱之后的事件,之前發(fā)生的事件將不會發(fā)送来屠。

example(of: "PublishSubject") {
    let subject = PublishSubject<String>()
    subject.onNext("Is anyone listening?")
    let subscriptionOne = subject.subscribe(onNext: { string in
            print(string)
        })
    subject.on(.next("1"))
    subject.onNext("2")
    
    let subscriptionTwo = subject
        .subscribe { event in
            print("2)", event.element ?? event)
    }
    subject.onNext("3")
    subscriptionOne.dispose()
    subject.onNext("4")
    
    // 1
    subject.onCompleted()
    // 2
    subject.onNext("5")
    // 3
    subscriptionTwo.dispose()
    let disposeBag = DisposeBag()
    // 4
    subject.subscribe {
            print("3)", $0.element ?? $0)
    }.addDisposableTo(disposeBag)
    subject.onNext("?")
}
//--- Example of: PublishSubject ---
//1
//2
//3
//2) 3
//2) 4
//2) completed
//3) completed

如果要保證所有事件都能被訂閱到虑椎,可以使用Create主動創(chuàng)建或使用ReplaySubject。
如果被觀察者因為錯誤被終止俱笛,PublishSubject只會發(fā)出一個錯誤的通知捆姜。

ReplaySubject

不管訂閱者什么時候訂閱的都可以把所有發(fā)生過的事件發(fā)送給訂閱者。

example(of: "ReplaySubject") {
    // 1
    let subject = ReplaySubject<String>.create(bufferSize: 2) //bufferSize指定緩沖區(qū)的大小
    let disposeBag = DisposeBag()
    // 2
    subject.onNext("1")
    subject.onNext("2")
    subject.onNext("3")
    // 3
    subject
        .subscribe {
            print(label: "1)", event: $0)
        }
        .addDisposableTo(disposeBag)
    subject
        .subscribe {
            print(label: "2)", event: $0)
        }
        .addDisposableTo(disposeBag)
    
    subject.onNext("4")
    subject.onError(MyError.anError)
    subject.dispose()

    subject
        .subscribe {
            print(label: "3)", event: $0)
    }
        .addDisposableTo(disposeBag)
    
}
//        --- Example of: ReplaySubject ---
//    1) 2
//    1) 3
//    2) 2
//    2) 3
//    1) 4
//    2) 4
//    1) anError
//    2) anError
//    3) Object `RxSwift.ReplayMany<Swift.String>` was already disposed.

BehaviorSubject

廣播所有事件給訂閱者迎膜,對于新的訂閱者泥技,廣播最近的一個事件或者默認(rèn)值

// 1
enum MyError: Error {
    case anError
}
// 2
func print<T: CustomStringConvertible>(label: String, event: Event<T>) {
    print(label, event.element ?? event.error ?? event)
}
// 3
example(of: "BehaviorSubject") {
    // 4
    let subject = BehaviorSubject(value: "Initial value")
    let disposeBag = DisposeBag()
    
    subject.subscribe {
            print(label: "1)", event: $0)
        }.addDisposableTo(disposeBag)
    subject.onNext("X")
    // 1
    subject.onError(MyError.anError)
    // 2
    subject.subscribe {
            print(label: "2)", event: $0)
        }.addDisposableTo(disposeBag)
}
//        --- Example of: BehaviorSubject ---
//    1) Initial value
//    1) X
//    1) anError
//    2) anError

PublishSubject, ReplaySubject, and BehaviorSubject當(dāng)他們被回收時,不會自動發(fā)送完成事件

Variable

Variable是BehaviorSubject的封裝磕仅,它和BehaviorSubject不同之處在于珊豹,不能向Variable發(fā)送.Complete和.Error,它會在生命周期結(jié)束被釋放的時候自動發(fā)送.Complete

example(of: "Variable") {
    // 1
    var variable = Variable("Initial value")
    let disposeBag = DisposeBag()
    // 2
    variable.value = "New initial value"
    // 3
    variable.asObservable()
        .subscribe {
            print(label: "1)", event: $0)
        }
        .addDisposableTo(disposeBag)
    // 1
    variable.value = "1"
    // 2
    variable.asObservable()
        .subscribe {
            print(label: "2)", event: $0)
        }
        .addDisposableTo(disposeBag)
    // 3
    variable.value = "2"
    
    // These will all generate errors
//    variable.value.onError(MyError.anError)
//    variable.asObservable().onError(MyError.anError)
//    variable.value = MyError.anError
//    variable.value.onCompleted()
//    variable.asObservable().onCompleted()
 
}
//    --- Example of: Variable ---
//1) New initial value
//1) 1
//2) 1
//1) 2
//2) 2

本文參考資料

RxSwift
RxSwift
alonemonkey

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宽涌,一起剝皮案震驚了整個濱河市平夜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卸亮,老刑警劉巖忽妒,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異兼贸,居然都是意外死亡段直,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門溶诞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸯檬,“玉大人,你說我怎么就攤上這事螺垢⌒瘢” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵枉圃,是天一觀的道長功茴。 經(jīng)常有香客問我,道長孽亲,這世上最難降的妖魔是什么坎穿? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上玲昧,老公的妹妹穿的比我還像新娘栖茉。我一直安慰自己,他們只是感情好孵延,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布吕漂。 她就那樣靜靜地躺著,像睡著了一般隙袁。 火紅的嫁衣襯著肌膚如雪挠唆。 梳的紋絲不亂的頭發(fā)上状原,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音鬓椭,去河邊找鬼鲸睛。 笑死娜饵,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的官辈。 我是一名探鬼主播箱舞,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拳亿!你這毒婦竟也來了晴股?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤肺魁,失蹤者是張志新(化名)和其女友劉穎电湘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹅经,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡寂呛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了瘾晃。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贷痪。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蹦误,靈堂內(nèi)的尸體忽然破棺而出劫拢,到底是詐尸還是另有隱情,我是刑警寧澤强胰,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布舱沧,位于F島的核電站,受9級特大地震影響哪廓,放射性物質(zhì)發(fā)生泄漏狗唉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一涡真、第九天 我趴在偏房一處隱蔽的房頂上張望分俯。 院中可真熱鬧肾筐,春花似錦、人聲如沸缸剪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杏节。三九已至唬渗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奋渔,已是汗流浹背镊逝。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留嫉鲸,地道東北人撑蒜。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像玄渗,于是被迫代替她去往敵國和親座菠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

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