RxSwift學(xué)習(xí)之十 (基礎(chǔ)使用篇 1- 序列客冈,訂閱,銷毀)

@[TOC](RxSwift學(xué)習(xí)之十 (基礎(chǔ)使用篇 1- 序列稳强,訂閱场仲,銷毀))

1. RxSwift簡介

  • RxSwift 的作用

1)在編寫代碼時(shí)我們經(jīng)常會(huì)需要檢測(cè)某些值的變化(比如:textFiled 輸入值的變化、數(shù)據(jù)請(qǐng)求完成或失敗的變化)退疫,然后進(jìn)行相應(yīng)的處理渠缕。
過去針對(duì)不同的情況,我們需要采用不同的事件傳遞方法去處理褒繁,比如:delegate亦鳞、notification、target-action棒坏、KVO 等等燕差。
而 RectiveX 機(jī)制(由 RxSwift 實(shí)現(xiàn))的出現(xiàn),讓程序里的事件傳遞響應(yīng)方法做到統(tǒng)一坝冕。將之前那些常用的事件傳遞方法(比如:delegate徒探、notification、target-action 等等)喂窟,全部替換成 Rx 的“信號(hào)鏈”方式测暗。
(2)如果我們平時(shí)使用的是 MVVM 開發(fā)模式的話,通過 RxSwift 可以獲得更加方便的數(shù)據(jù)綁定的方法磨澡,使得 MVVM 開發(fā)更加如虎添翼碗啄。

  • RxSwift 與 RxCocoa

RxSwift:它只是基于 Swift 語言的 Rx 標(biāo)準(zhǔn)實(shí)現(xiàn)接口庫,所以 RxSwift 里不包含任何 Cocoa 或者 UI 方面的類钱贯。
RxCocoa:是基于 RxSwift 針對(duì)于 iOS 開發(fā)的一個(gè)庫挫掏,它通過 Extension 的方法給原生的比如 UI 控件添加了 Rx 的特性,使得我們更容易訂閱和響應(yīng)這些控件的事件秩命。

2. RxSwift簡單使用

2.1 響應(yīng)式編程與傳統(tǒng)式編程的比較樣例

  • 實(shí)例2.1
  1. 有這么一個(gè)需求:
    表格中顯示的是歌曲信息(歌名尉共,以及歌手)
    點(diǎn)擊選中任意一個(gè)單元格,在控制臺(tái)中打印出對(duì)應(yīng)的歌曲信息弃锐。


    在這里插入圖片描述
  2. 按傳統(tǒng)方式:首先我們創(chuàng)建一個(gè) Music 的結(jié)構(gòu)體袄友,用來保存歌曲名稱、歌手名字霹菊。此外它還遵循 CustomStringConvertible 協(xié)議剧蚣,方便我們輸出調(diào)試支竹。
import UIKit
 
//歌曲結(jié)構(gòu)體
struct Music {
    let name: String //歌名
    let singer: String //演唱者
     
    init(name: String, singer: String) {
        self.name = name
        self.singer = singer
    }
}
 
//實(shí)現(xiàn) CustomStringConvertible 協(xié)議,方便輸出調(diào)試
extension Music: CustomStringConvertible {
    var description: String {
        return "name:\(name) singer:\(singer)"
    }
}

2.1.1 傳統(tǒng)編程

  1. 首先寫一個(gè) ViewModel
import Foundation
 
//歌曲列表數(shù)據(jù)源
struct MusicListViewModel {
    let data = [
        Music(name: "無條件", singer: "陳奕迅"),
        Music(name: "你曾是少年", singer: "S.H.E"),
        Music(name: "從前的我", singer: "陳潔儀"),
        Music(name: "在木星", singer: "樸樹"),
    ]
}
  1. 視圖控制器代碼(ViewController.swift)
  • 接著我們?cè)O(shè)置 UITableView 的委托鸠按,并讓視圖控制器實(shí)現(xiàn) UITableViewDataSource 和 UITableViewDelegate 協(xié)議礼搁,及相關(guān)的協(xié)議方法。
  • 這個(gè)大家肯定都寫過無數(shù)遍了目尖,也沒什么好講的馒吴。算一下,這里一共需要 43 行代碼瑟曲。
import UIKit
import RxSwift
 
class ViewController: UIViewController {
 
    //tableView對(duì)象
    @IBOutlet weak var tableView: UITableView!
     
    //歌曲列表數(shù)據(jù)源
    let musicListViewModel = MusicListViewModel()
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        //設(shè)置代理
        tableView.dataSource = self
        tableView.delegate = self
    }
}
 
extension ViewController: UITableViewDataSource {
    //返回單元格數(shù)量
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return musicListViewModel.data.count
    }
     
    //返回對(duì)應(yīng)的單元格
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
        -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "musicCell")!
        let music = musicListViewModel.data[indexPath.row]
        cell.textLabel?.text = music.name
        cell.detailTextLabel?.text = music.singer
        return cell
    }
}
 
extension ViewController: UITableViewDelegate {
    //單元格點(diǎn)擊
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("你選中的歌曲信息【\(musicListViewModel.data[indexPath.row])】")
    }
}
  • 下面來看一下Rxswift的編程

2.1.2 Rxswift編程

  1. 對(duì) ViewModel 做些修改
    • 這里我們將 data 屬性變成一個(gè)可觀察序列對(duì)象(Observable Squence)饮戳,而對(duì)象當(dāng)中的內(nèi)容和我們之前在數(shù)組當(dāng)中所包含的內(nèi)容是完全一樣的。
    • 關(guān)于可觀察序列對(duì)象在后面的文章中我會(huì)詳細(xì)介紹洞拨。簡單說就是“序列”可以對(duì)這些數(shù)值進(jìn)行“訂閱(Subscribe)”扯罐,有點(diǎn)類似于“通知(NotificationCenter)”
import RxSwift
 
//歌曲列表數(shù)據(jù)源
struct MusicListViewModel {
    let data = Observable.just([
        Music(name: "無條件", singer: "陳奕迅"),
        Music(name: "你曾是少年", singer: "S.H.E"),
        Music(name: "從前的我", singer: "陳潔儀"),
        Music(name: "在木星", singer: "樸樹"),
    ])
}
  1. 視圖控制器代碼(ViewController.swift
    • 這里我們不再需要實(shí)現(xiàn)數(shù)據(jù)源和委托協(xié)議了。而是寫一些響應(yīng)式代碼烦衣,讓它們將數(shù)據(jù)和 UITableView 建立綁定關(guān)系歹河。
    • 算了下這里我們只需要 31 行代碼,同之前的相比琉挖,一下減少了 1/4 代碼量启泣。而且代碼也更清爽了些。
    • 代碼的簡單說明:
      DisposeBag:作用是 Rx 在視圖控制器或者其持有者將要銷毀的時(shí)候示辈,自動(dòng)釋法掉綁定在它上面的資源寥茫。它是通過類似“訂閱處置機(jī)制”方式實(shí)現(xiàn)(類似于 NotificationCenter 的 removeObserver)。
      rx.items(cellIdentifier:):這是 Rx 基于 cellForRowAt 數(shù)據(jù)源方法的一個(gè)封裝矾麻。傳統(tǒng)方式中我們還要有個(gè) numberOfRowsInSection 方法纱耻,使用 Rx 后就不再需要了(Rx 已經(jīng)幫我們完成了相關(guān)工作)。
      rx.modelSelected: 這是 Rx 基于 UITableView 委托回調(diào)方法 didSelectRowAt 的一個(gè)封裝险耀。

import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
 
    //tableView對(duì)象
    @IBOutlet weak var tableView: UITableView!
     
    //歌曲列表數(shù)據(jù)源
    let musicListViewModel = MusicListViewModel()
     
    //負(fù)責(zé)對(duì)象銷毀
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        //將數(shù)據(jù)源數(shù)據(jù)綁定到tableView上
        musicListViewModel.data
            .bind(to: tableView.rx.items(cellIdentifier:"musicCell")) { _, music, cell in
                cell.textLabel?.text = music.name
                cell.detailTextLabel?.text = music.singer
            }.disposed(by: disposeBag)
         
        //tableView點(diǎn)擊響應(yīng)
        tableView.rx.modelSelected(Music.self).subscribe(onNext: { music in
            print("你選中的歌曲信息【\(music)】")
        }).disposed(by: disposeBag)
    }
}

2.2 Observable介紹弄喘、創(chuàng)建可觀察序列

  • Observable 作為 Rx 的根基,我們首先對(duì)它要有一些基本的了解甩牺。

  • Observable<T>:

    Observable<T> 這個(gè)類就是 Rx 框架的基礎(chǔ)蘑志,我們可以稱它為可觀察序列。它的作用就是可以異步地產(chǎn)生一系列的 Event(事件)贬派,即一個(gè) Observable<T> 對(duì)象會(huì)隨著時(shí)間推移不定期地發(fā)出 event(element : T) 這樣一個(gè)東西急但。
    而且這些 Event 還可以攜帶數(shù)據(jù),它的泛型 <T> 就是用來指定這個(gè) Event 攜帶的數(shù)據(jù)的類型搞乏。
    有了可觀察序列波桩,我們還需要有一個(gè) Observer(訂閱者)來訂閱它,這樣這個(gè)訂閱者才能收到 Observable<T> 不時(shí)發(fā)出的 Event请敦。

  • Event

  • 查看 RxSwift 源碼可以發(fā)現(xiàn)镐躲,事件 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 事件:
    • nextnext 事件就是那個(gè)可以攜帶數(shù)據(jù) <T> 的事件,可以說它就是一個(gè)“最正秤┰恚”的事件撒穷。
    • errorerror 事件表示一個(gè)錯(cuò)誤,它可以攜帶具體的錯(cuò)誤內(nèi)容敌蚜,一旦 Observable 發(fā)出了 error event桥滨,則這個(gè) Observable 就等于終止了窝爪,以后它再也不會(huì)發(fā)出 event 事件了弛车。
    • completedcompleted 事件表示 Observable 發(fā)出的事件正常地結(jié)束了,跟 error 一樣蒲每,一旦 Observable 發(fā)出了 completed event纷跛,則這個(gè) Observable 就等于終止了,以后它再也不會(huì)發(fā)出 event 事件了邀杏。

2.2.1 Observable 與 Sequence比較

  • 1)為更好地理解贫奠,我們可以把每一個(gè) Observable 的實(shí)例想象成于一個(gè) Swift 中的 Sequence:
    • 即一個(gè) ObservableObservableType)相當(dāng)于一個(gè)序列 SequenceSequenceType)。
    • ObservableType.subscribe(_:) 方法其實(shí)就相當(dāng)于 SequenceType.generate()
  • 2)但它們之間還是有許多區(qū)別的:
    • Swift 中的 SequenceType 是同步的循環(huán)望蜡,而 Observable 是異步的唤崭。
    • Observable 對(duì)象會(huì)在有任何 Event 時(shí)候,自動(dòng)將 Event 作為一個(gè)參數(shù)通過 ObservableType.subscribe(_:) 發(fā)出脖律,并不需要使用 next 方法谢肾。

2.2.2 創(chuàng)建 Observable 序列

  • 我們可以通過如下幾種方法來創(chuàng)建一個(gè) Observable 序列
  1. just() 方法

(1)該方法通過傳入一個(gè)默認(rèn)值來初始化。
(2)下面樣例我們顯式地標(biāo)注出了 observable 的類型為 Observable<Int>小泉,即指定了這個(gè) Observable 所發(fā)出的事件攜帶的數(shù)據(jù)類型必須是 Int 類型的芦疏。

let observable = Observable<Int>.just(5)
  1. of() 方法

(1)該方法可以接受可變數(shù)量的參數(shù)(必需要是同類型的)
(2)下面樣例中我沒有顯式地聲明出 Observable 的泛型類型,Swift 也會(huì)自動(dòng)推斷類型微姊。

let observable = Observable.of("A", "B", "C")
  1. from() 方法

(1)該方法需要一個(gè)數(shù)組參數(shù)酸茴。
(2)下面樣例中數(shù)據(jù)里的元素就會(huì)被當(dāng)做這個(gè) Observable 所發(fā)出 event 攜帶的數(shù)據(jù)內(nèi)容,最終效果同上面餓 of() 樣例是一樣的兢交。

let observable = Observable.from(["A", "B", "C"])
  1. empty() 方法
    該方法創(chuàng)建一個(gè)空內(nèi)容的 Observable 序列薪捍。
let observable = Observable<Int>.never()
  1. never() 方法
    該方法創(chuàng)建一個(gè)永遠(yuǎn)不會(huì)發(fā)出 Event(也不會(huì)終止)的 Observable 序列。
let observable = Observable<Int>.never()
  1. error() 方法
    該方法創(chuàng)建一個(gè)不做任何操作配喳,而是直接發(fā)送一個(gè)錯(cuò)誤的 Observable 序列酪穿。
enum MyError: Error {
    case A
    case B
}
         
let observable = Observable<Int>.error(MyError.A)
  1. range() 方法

(1)該方法通過指定起始和結(jié)束數(shù)值,創(chuàng)建一個(gè)以這個(gè)范圍內(nèi)所有值作為初始值的 Observable 序列界逛。

(2)下面樣例中昆稿,兩種方法創(chuàng)建的 Observable 序列都是一樣的。

//使用range()
let observable = Observable.range(start: 1, count: 5)
 
//使用of()
let observable = Observable.of(1, 2, 3 ,4 ,5)
  1. repeatElement() 方法
    該方法創(chuàng)建一個(gè)可以無限發(fā)出給定元素的 Event 的 Observable 序列(永不終止)息拜。
let observable = Observable.repeatElement(1)
  1. generate() 方法

(1)該方法創(chuàng)建一個(gè)只有當(dāng)提供的所有的判斷條件都為 true 的時(shí)候溉潭,才會(huì)給出動(dòng)作的 Observable 序列净响。
(2)下面樣例中,兩種方法創(chuàng)建的 Observable 序列都是一樣的喳瓣。

//使用generate()方法
let observable = Observable.generate(
    initialState: 0,
    condition: { $0 <= 10 },
    iterate: { $0 + 2 }
)
 
//使用of()方法
let observable = Observable.of(0 , 2 ,4 ,6 ,8 ,10)
  1. create() 方法

(1)該方法接受一個(gè) block 形式的參數(shù)馋贤,任務(wù)是對(duì)每一個(gè)過來的訂閱進(jìn)行處理。
(2)下面是一個(gè)簡單的樣例畏陕。為方便演示配乓,這里增加了訂閱相關(guān)代碼

//這個(gè)block有一個(gè)回調(diào)參數(shù)observer就是訂閱這個(gè)Observable對(duì)象的訂閱者
//當(dāng)一個(gè)訂閱者訂閱這個(gè)Observable對(duì)象的時(shí)候,就會(huì)將訂閱者作為參數(shù)傳入這個(gè)block來執(zhí)行一些內(nèi)容
let observable = Observable<String>.create{observer in
    //對(duì)訂閱者發(fā)出了.next事件惠毁,且攜帶了一個(gè)數(shù)據(jù)"hangge.com"
    observer.onNext("hangge.com")
    //對(duì)訂閱者發(fā)出了.completed事件
    observer.onCompleted()
    //因?yàn)橐粋€(gè)訂閱行為會(huì)有一個(gè)Disposable類型的返回值犹芹,所以在結(jié)尾一定要returen一個(gè)Disposable
    return Disposables.create()
}
 
//訂閱測(cè)試
observable.subscribe {
    print($0)
}
  1. deferred() 方法

(1)該個(gè)方法相當(dāng)于是創(chuàng)建一個(gè) Observable 工廠,通過傳入一個(gè) block 來執(zhí)行延遲 Observable 序列創(chuàng)建的行為鞠绰,而這個(gè) block 里就是真正的實(shí)例化序列對(duì)象的地方腰埂。
(2)下面是一個(gè)簡單的演示樣例:

//用于標(biāo)記是奇數(shù)、還是偶數(shù)
var isOdd = true
 
//使用deferred()方法延遲Observable序列的初始化蜈膨,通過傳入的block來實(shí)現(xiàn)Observable序列的初始化并且返回屿笼。
let factory : Observable<Int> = Observable.deferred {
     
    //讓每次執(zhí)行這個(gè)block時(shí)候都會(huì)讓奇、偶數(shù)進(jìn)行交替
    isOdd = !isOdd
     
    //根據(jù)isOdd參數(shù)翁巍,決定創(chuàng)建并返回的是奇數(shù)Observable驴一、還是偶數(shù)Observable
    if isOdd {
        return Observable.of(1, 3, 5 ,7)
    }else {
        return Observable.of(2, 4, 6, 8)
    }
}
 
//第1次訂閱測(cè)試
factory.subscribe { event in
    print("\(isOdd)", event)
}
 
//第2次訂閱測(cè)試
factory.subscribe { event in
    print("\(isOdd)", event)
}

運(yùn)行結(jié)果如下,可以看到我們兩次訂閱的得到的 Observable 是不一樣的:


在這里插入圖片描述
  1. interval() 方法

(1)這個(gè)方法創(chuàng)建的 Observable 序列每隔一段設(shè)定的時(shí)間灶壶,會(huì)發(fā)出一個(gè)索引數(shù)的元素肝断。而且它會(huì)一直發(fā)送下去。
(2)下面方法讓其每 1 秒發(fā)送一次例朱,并且是在主線程(MainScheduler)發(fā)送孝情。

let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable.subscribe { event in
    print(event)
}
  1. timer() 方法
  • (1) 這個(gè)方法有兩種用法,一種是創(chuàng)建的 Observable 序列在經(jīng)過設(shè)定的一段時(shí)間后洒嗤,產(chǎn)生唯一的一個(gè)元素箫荡。
//5秒種后發(fā)出唯一的一個(gè)元素0
let observable = Observable<Int>.timer(5, scheduler: MainScheduler.instance)
observable.subscribe { event in
    print(event)
}
  • (2) 另一種是創(chuàng)建的 Observable 序列在經(jīng)過設(shè)定的一段時(shí)間后,每隔一段時(shí)間產(chǎn)生一個(gè)元素渔隶。
//延時(shí)5秒種后羔挡,每隔1秒鐘發(fā)出一個(gè)元素
let observable = Observable<Int>.timer(5, period: 1, scheduler: MainScheduler.instance)
observable.subscribe { event in
    print(event)
}

2.3 Observable訂閱、事件監(jiān)聽间唉、訂閱銷毀

2.3.1 Observable訂閱

  • 有了 Observable绞灼,我們還要使用 subscribe() 方法來訂閱它,接收它發(fā)出的 Event呈野。

2.3.1.1 第一種訂閱方式

  • (1)我們使用 subscribe() 訂閱了一個(gè) Observable 對(duì)象低矮,該方法的 block 的回調(diào)參數(shù)就是被發(fā)出的 event 事件,我們將其直接打印出來被冒。
let observable = Observable.of("A", "B", "C")
         
observable.subscribe { event in
    print(event)
}

運(yùn)行結(jié)果如下军掂,可以看到:

image

初始化 Observable 序列時(shí)設(shè)置的默認(rèn)值都按順序通過 .next 事件發(fā)送出來轮蜕。
當(dāng) Observable 序列的初始數(shù)據(jù)都發(fā)送完畢,它還會(huì)自動(dòng)發(fā)一個(gè) .completed 事件出來蝗锥。

  • (2)如果想要獲取到這個(gè)事件里的數(shù)據(jù)跃洛,可以通過 event.element 得到。
let observable = Observable.of("A", "B", "C")
         
observable.subscribe { event in
    print(event.element)
}

運(yùn)行結(jié)果如下:


image

2.3.1.2 第二種訂閱方式

  • (1)RxSwift 還提供了另一個(gè) subscribe 方法终议,它可以把 event 進(jìn)行分類:
    • 通過不同的 block 回調(diào)處理不同類型的 event汇竭。(其中 onDisposed 表示訂閱行為被 dispose 后的回調(diào))
    • 同時(shí)會(huì)把 event 攜帶的數(shù)據(jù)直接解包出來作為參數(shù),方便我們使用穴张。
let observable = Observable.of("A", "B", "C")
         
observable.subscribe(onNext: { element in
    print(element)
}, onError: { error in
    print(error)
}, onCompleted: {
    print("completed")
}, onDisposed: {
    print("disposed")
})

運(yùn)行結(jié)果如下:


image
  • (2)subscribe() 方法的 onNext细燎、onErroronCompletedonDisposed 這四個(gè)回調(diào) block 參數(shù)都是有默認(rèn)值的陆馁,即它們都是可選的找颓。所以我們也可以只處理 onNext 而不管其他的情況。
let observable = Observable.of("A", "B", "C")
         
observable.subscribe(onNext: { element in
    print(element)
})

運(yùn)行結(jié)果如下:
A
B
C

2.3.2 事件監(jiān)聽

  • doOn 介紹

    (1)我們可以使用 doOn 方法來監(jiān)聽事件的生命周期叮贩,它會(huì)在每一次事件發(fā)送前被調(diào)用。
    (2)同時(shí)它和 subscribe 一樣佛析,可以通過不同的 block 回調(diào)處理不同類型的 event益老。比如:
    do(onNext:) 方法就是在 subscribe(onNext:) 前調(diào)用
    而 do(onCompleted:) 方法則會(huì)在 subscribe(onCompleted:) 前面調(diào)用。

  • 使用樣例

let observable = Observable.of("A", "B", "C")
 
observable
    .do(onNext: { element in
        print("Intercepted Next:", element)
    }, onError: { error in
        print("Intercepted Error:", error)
    }, onCompleted: {
        print("Intercepted Completed")
    }, onDispose: {
        print("Intercepted Disposed")
    })
    .subscribe(onNext: { element in
        print(element)
    }, onError: { error in
        print(error)
    }, onCompleted: {
        print("completed")
    }, onDisposed: {
        print("disposed")
    })

2.3.3 訂閱銷毀

2.3.3.1 Observable 從創(chuàng)建到終結(jié)流程

  • (1)一個(gè) Observable 序列被創(chuàng)建出來后它不會(huì)馬上就開始被激活從而發(fā)出 Event寸莫,而是要等到它被某個(gè)人訂閱了才會(huì)激活它捺萌。
  • (2)而 Observable 序列激活之后要一直等到它發(fā)出了 .error 或者 .completedevent 后,它才被終結(jié)膘茎。

2.3.3.2 dispose() 方法

-(1)使用該方法我們可以手動(dòng)取消一個(gè)訂閱行為桃纯。
-(2)如果我們覺得這個(gè)訂閱結(jié)束了不再需要了,就可以調(diào)用 dispose() 方法把這個(gè)訂閱給銷毀掉披坏,防止內(nèi)存泄漏态坦。
-(3)當(dāng)一個(gè)訂閱行為被 dispose 了,那么之后 observable 如果再發(fā)出 event棒拂,這個(gè)已經(jīng) dispose 的訂閱就收不到消息了伞梯。下面是一個(gè)簡單的使用樣例。

let observable = Observable.of("A", "B", "C")
         
//使用subscription常量存儲(chǔ)這個(gè)訂閱方法
let subscription = observable.subscribe { event in
    print(event)
}
         
//調(diào)用這個(gè)訂閱的dispose()方法
subscription.dispose()

2.3.3.13 DisposeBag

  • (1)除了 dispose() 方法之外帚屉,我們更經(jīng)常用到的是一個(gè)叫 DisposeBag 的對(duì)象來管理多個(gè)訂閱行為的銷毀:
  1. 我們可以把一個(gè) DisposeBag 對(duì)象看成一個(gè)垃圾袋谜诫,把用過的訂閱行為都放進(jìn)去。
  2. 而這個(gè) DisposeBag 就會(huì)在自己快要 dealloc 的時(shí)候攻旦,對(duì)它里面的所有訂閱行為都調(diào)用 dispose() 方法喻旷。
  • (2)下面是一個(gè)簡單的使用樣例。
let disposeBag = DisposeBag()
         
//第1個(gè)Observable牢屋,及其訂閱
let observable1 = Observable.of("A", "B", "C")
observable1.subscribe { event in
    print(event)
}.disposed(by: disposeBag)
 
//第2個(gè)Observable且预,及其訂閱
let observable2 = Observable.of(1, 2, 3)
observable2.subscribe { event in
    print(event)
}.disposed(by: disposeBag)

2.4 AnyObserver牺陶、Binder

2.4.1 觀察者(Observer)

  • 觀察者(Observer)的作用就是監(jiān)聽事件,然后對(duì)這個(gè)事件做出響應(yīng)辣之£欤或者說任何響應(yīng)事件的行為都是觀察者。比如:
    • 當(dāng)我們點(diǎn)擊按鈕怀估,彈出一個(gè)提示框狮鸭。那么這個(gè)“彈出一個(gè)提示框”就是觀察者 Observer<Void>
    • 當(dāng)我們請(qǐng)求一個(gè)遠(yuǎn)程的 json 數(shù)據(jù)后,將其打印出來多搀。那么這個(gè)“打印 json 數(shù)據(jù)”就是觀察者 Observer<JSON>
image

2.4.2 創(chuàng)建觀察者

2.4.2.1 直接在 subscribe歧蕉、bind 方法中創(chuàng)建觀察者

  1. 在 subscribe 方法中創(chuàng)建

(1)創(chuàng)建觀察者最直接的方法就是在 Observable 的 subscribe 方法后面描述當(dāng)事件發(fā)生時(shí),需要如何做出響應(yīng)康铭。
(2)比如下面的樣例惯退,觀察者就是由后面的 onNext,onError从藤,onCompleted 這些閉包構(gòu)建出來的催跪。

let observable = Observable.of("A", "B", "C")
          
observable.subscribe(onNext: { element in
    print(element)
}, onError: { error in
    print(error)
}, onCompleted: {
    print("completed")
})

運(yùn)行結(jié)果:


image
  1. 在 bind 方法中創(chuàng)建
    (1)下面代碼我們創(chuàng)建一個(gè)定時(shí)生成索引數(shù)的 Observable 序列,并將索引數(shù)不斷顯示在 label 標(biāo)簽上:
//Observable序列(每隔1秒鐘發(fā)出一個(gè)索引數(shù))
        let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
 
        observable
            .map { "當(dāng)前索引數(shù):\($0 )"}
            .bind { [weak self](text) in
                //收到發(fā)出的索引數(shù)后顯示到label上
                self?.label.text = text
            }
            .disposed(by: disposeBag)

2.4.2.2 使用 AnyObserver 創(chuàng)建觀察者

  • AnyObserver 可以用來描敘任意一種觀察者夷野。
  • 配合 subscribe 方法使用
//觀察者
let observer: AnyObserver<String> = AnyObserver { (event) in
    switch event {
    case .next(let data):
        print(data)
    case .error(let error):
        print(error)
    case .completed:
        print("completed")
    }
}
 
let observable = Observable.of("A", "B", "C")
observable.subscribe(observer)
  • 配合 bindTo 方法使用
 //觀察者
        let observer: AnyObserver<String> = AnyObserver { [weak self] (event) in
            switch event {
            case .next(let text):
                //收到發(fā)出的索引數(shù)后顯示到label上
                self?.label.text = text
            default:
                break
            }
        }
         
        //Observable序列(每隔1秒鐘發(fā)出一個(gè)索引數(shù))
        let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
        observable
            .map { "當(dāng)前索引數(shù):\($0 )"}
            .bind(to: observer)
            .disposed(by: disposeBag)
    }

2.4.2.3 使用 Binder 創(chuàng)建觀察者

(1)相較于 AnyObserver 的大而全懊蒸,Binder 更專注于特定的場(chǎng)景悔常。Binder 主要有以下兩個(gè)特征:
不會(huì)處理錯(cuò)誤事件
確保綁定都是在給定 Scheduler 上執(zhí)行(默認(rèn) MainScheduler)
(2)一旦產(chǎn)生錯(cuò)誤事件米酬,在調(diào)試環(huán)境下將執(zhí)行 fatalError,在發(fā)布環(huán)境下將打印錯(cuò)誤信息恭取。

  • 實(shí)例2.4.2.3
       //觀察者
        let observer: Binder<String> = Binder(label) { (view, text) in
            //收到發(fā)出的索引數(shù)后顯示到label上
            view.text = text
        }
         
        //Observable序列(每隔1秒鐘發(fā)出一個(gè)索引數(shù))
        let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
        observable
            .map { "當(dāng)前索引數(shù):\($0 )"}
            .bind(to: observer)
            .disposed(by: disposeBag)

//Observable序列(每隔1秒鐘發(fā)出一個(gè)索引數(shù))
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable
    .map { $0 % 2 == 0 }
    .bind(to: button.rx.isEnabled)
    .disposed(by: disposeBag)

2.5 自定義可綁定屬性

  • 有時(shí)我們想讓 UI 控件創(chuàng)建出來后默認(rèn)就有一些觀察者妒貌,而不必每次都為它們單獨(dú)去創(chuàng)建觀察者通危。比如我們想要讓所有的 UIlabel 都有個(gè) fontSize 可綁定屬性,它會(huì)根據(jù)事件值自動(dòng)改變標(biāo)簽的字體大小灌曙。

  • 通過對(duì) UI 類進(jìn)行擴(kuò)展
    這里我們通過對(duì) UILabel 進(jìn)行擴(kuò)展菊碟,增加了一個(gè) fontSize 可綁定屬性。

import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    @IBOutlet weak var label: UILabel!
     
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
         
        //Observable序列(每隔0.5秒鐘發(fā)出一個(gè)索引數(shù))
        let observable = Observable<Int>.interval(0.5, scheduler: MainScheduler.instance)
        observable
            .map { CGFloat($0) }
            .bind(to: label.fontSize) //根據(jù)索引數(shù)不斷變放大字體
            .disposed(by: disposeBag)
    }
}
 
extension UILabel {
    public var fontSize: Binder<CGFloat> {
        return Binder(self) { label, fontSize in
            label.font = UIFont.systemFont(ofSize: fontSize)
        }
    }
}
  • 通過對(duì) Reactive 類進(jìn)行擴(kuò)展
    既然使用了 RxSwift平匈,那么更規(guī)范的寫法應(yīng)該是對(duì) Reactive 進(jìn)行擴(kuò)展框沟。這里同樣是給 UILabel 增加了一個(gè) fontSize 可綁定屬性。
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    @IBOutlet weak var label: UILabel!
     
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
         
        //Observable序列(每隔0.5秒鐘發(fā)出一個(gè)索引數(shù))
        let observable = Observable<Int>.interval(0.5, scheduler: MainScheduler.instance)
        observable
            .map { CGFloat($0) }
            .bind(to: label.rx.fontSize) //根據(jù)索引數(shù)不斷變放大字體
            .disposed(by: disposeBag)
    }
}
 
extension Reactive where Base: UILabel {
    public var fontSize: Binder<CGFloat> {
        return Binder(self.base) { label, fontSize in
            label.font = UIFont.systemFont(ofSize: fontSize)
        }
    }
}
  • RxSwift 自帶的可綁定屬性(UI 觀察者)

其實(shí) RxSwift 已經(jīng)為我們提供許多常用的可綁定屬性增炭。比如 UILabel 就有 text 和 attributedText 這兩個(gè)可綁定屬性忍燥。

extension Reactive where Base: UILabel {
     
    /// Bindable sink for `text` property.
    public var text: Binder<String?> {
        return Binder(self.base) { label, text in
            label.text = text
        }
    }
 
    /// Bindable sink for `attributedText` property.
    public var attributedText: Binder<NSAttributedString?> {
        return Binder(self.base) { label, text in
            label.attributedText = text
        }
    }
}

那么上文那個(gè)定時(shí)顯示索引數(shù)的樣例,我們其實(shí)不需要自定義 UI 觀察者隙姿,直接使用 RxSwift 提供的綁定屬性即可梅垄。

//Observable序列(每隔1秒鐘發(fā)出一個(gè)索引數(shù))
        let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
        observable
            .map { "當(dāng)前索引數(shù):\($0 )"}
            .bind(to: label.rx.text) //收到發(fā)出的索引數(shù)后顯示到label上
            .disposed(by: disposeBag)

2.6 Subjects、Variables

當(dāng)我們創(chuàng)建一個(gè) Observable 的時(shí)候就要預(yù)先將要發(fā)出的數(shù)據(jù)都準(zhǔn)備好,等到有人訂閱它時(shí)再將數(shù)據(jù)通過 Event 發(fā)出去队丝。
但有時(shí)我們希望 Observable 在運(yùn)行時(shí)能動(dòng)態(tài)地“獲得”或者說“產(chǎn)生”出一個(gè)新的數(shù)據(jù)靡馁,再通過 Event 發(fā)送出去。比如:訂閱一個(gè)輸入框的輸入內(nèi)容机久,當(dāng)用戶每輸入一個(gè)字后臭墨,這個(gè)輸入框關(guān)聯(lián)的 Observable 就會(huì)發(fā)出一個(gè)帶有輸入內(nèi)容的 Event,通知給所有訂閱者膘盖。
這個(gè)就可以使用下面將要介紹的 Subjects 來實(shí)現(xiàn)胧弛。

2.6.1 Subjects

2.6.1.1 Subjects 簡介

-(1)Subjects 既是訂閱者,也是 Observable:
說它是訂閱者侠畔,是因?yàn)樗軌騽?dòng)態(tài)地接收新的值结缚。
說它又是一個(gè) Observable,是因?yàn)楫?dāng) Subjects 有了新的值之后软棺,就會(huì)通過 Event 將新值發(fā)出給他的所有訂閱者红竭。

-(2)一共有四種 Subjects,分別為:PublishSubject喘落、BehaviorSubject茵宪、ReplaySubjectVariable揖盘。他們之間既有各自的特點(diǎn)眉厨,也有相同之處:

  1. 首先他們都是 Observable,他們的訂閱者都能收到他們發(fā)出的新的 Event兽狭。
  2. 直到 Subject 發(fā)出 .complete 或者 .errorEvent 后,該 Subject 便終結(jié)了鹿蜀,同時(shí)它也就不會(huì)再發(fā)出 .next 事件箕慧。
  3. 對(duì)于那些在 Subject 終結(jié)后再訂閱他的訂閱者,也能收到 subject 發(fā)出的一條 .complete.errorevent茴恰,告訴這個(gè)新的訂閱者它已經(jīng)終結(jié)了颠焦。
  4. .他們之間最大的區(qū)別只是在于:當(dāng)一個(gè)新的訂閱者剛訂閱它的時(shí)候,能不能收到 Subject 以前發(fā)出過的舊 Event往枣,如果能的話又能收到多少個(gè)伐庭。

-(3)Subject 常用的幾個(gè)方法:

  1. onNext(:):是 on(.next(:)) 的簡便寫法。該方法相當(dāng)于 subject 接收到一個(gè) .next 事件分冈。
  2. onError(:):是 on(.error(:)) 的簡便寫法圾另。該方法相當(dāng)于 subject 接收到一個(gè) .error 事件。
  3. onCompleted():是 on(.completed) 的簡便寫法雕沉。該方法相當(dāng)于 subject 接收到一個(gè) .completed 事件集乔。

2.6.1.2 PublishSubject

  • (1)基本介紹

PublishSubject 是最普通的 Subject,它不需要初始值就能創(chuàng)建坡椒。
PublishSubject 的訂閱者從他們開始訂閱的時(shí)間點(diǎn)起扰路,可以收到訂閱后 Subject 發(fā)出的新 Event尤溜,而不會(huì)收到他們?cè)谟嗛喦耙寻l(fā)出的 Event

  • (2)時(shí)序圖
    如下圖:最上面一條是 PublishSubject汗唱。
    下面兩條分別表示兩個(gè)新的訂閱宫莱,它們訂閱的時(shí)間點(diǎn)不同,可以發(fā)現(xiàn) PublishSubject 的訂閱者只能收到他們訂閱后的 Event哩罪。
image
  • 實(shí)例 2.6.1.2 :
let disposeBag = DisposeBag()
 
//創(chuàng)建一個(gè)PublishSubject
let subject = PublishSubject<String>()
 
//由于當(dāng)前沒有任何訂閱者授霸,所以這條信息不會(huì)輸出到控制臺(tái)
subject.onNext("111")
 
//第1次訂閱subject
subject.subscribe(onNext: { string in
    print("第1次訂閱:", string)
}, onCompleted:{
    print("第1次訂閱:onCompleted")
}).disposed(by: disposeBag)
 
//當(dāng)前有1個(gè)訂閱,則該信息會(huì)輸出到控制臺(tái)
subject.onNext("222")
 
//第2次訂閱subject
subject.subscribe(onNext: { string in
    print("第2次訂閱:", string)
}, onCompleted:{
    print("第2次訂閱:onCompleted")
}).disposed(by: disposeBag)
 
//當(dāng)前有2個(gè)訂閱识椰,則該信息會(huì)輸出到控制臺(tái)
subject.onNext("333")
 
//讓subject結(jié)束
subject.onCompleted()
 
//subject完成后會(huì)發(fā)出.next事件了绝葡。
subject.onNext("444")
 
//subject完成后它的所有訂閱(包括結(jié)束后的訂閱),都能收到subject的.completed事件腹鹉,
subject.subscribe(onNext: { string in
    print("第3次訂閱:", string)
}, onCompleted:{
    print("第3次訂閱:onCompleted")
}).disposed(by: disposeBag)

運(yùn)行結(jié)果:

image

2.6.1.3 BehaviorSubject

-(1)基本介紹

BehaviorSubject 需要通過一個(gè)默認(rèn)初始值來創(chuàng)建藏畅。
當(dāng)一個(gè)訂閱者來訂閱它的時(shí)候,這個(gè)訂閱者會(huì)立即收到 BehaviorSubjects 上一個(gè)發(fā)出的 event功咒。之后就跟正常的情況一樣愉阎,它也會(huì)接收到 BehaviorSubject 之后發(fā)出的新的 event

-(2)時(shí)序圖
如下圖:最上面一條是 BehaviorSubject力奋。
下面兩條分別表示兩個(gè)新的訂閱榜旦,它們訂閱的時(shí)間點(diǎn)不同,可以發(fā)現(xiàn) BehaviorSubject 的訂閱者一開始就能收到 BehaviorSubjects 之前發(fā)出的一個(gè) Event景殷。

BehaviorSubject
  • 實(shí)例 2.6.1.3
let disposeBag = DisposeBag()
 
//創(chuàng)建一個(gè)BehaviorSubject
let subject = BehaviorSubject(value: "111")
 
//第1次訂閱subject
subject.subscribe { event in
    print("第1次訂閱:", event)
}.disposed(by: disposeBag)
 
//發(fā)送next事件
subject.onNext("222")
 
//發(fā)送error事件
subject.onError(NSError(domain: "local", code: 0, userInfo: nil))
 
//第2次訂閱subject
subject.subscribe { event in
    print("第2次訂閱:", event)
}.disposed(by: disposeBag)

運(yùn)行結(jié)果:


image

2.6.1.4 ReplaySubject

  • (1)基本介紹
  1. ReplaySubject 在創(chuàng)建時(shí)候需要設(shè)置一個(gè) bufferSize溅呢,表示它對(duì)于它發(fā)送過的 event 的緩存?zhèn)€數(shù)。
  2. 比如一個(gè) ReplaySubjectbufferSize 設(shè)置為 2猿挚,它發(fā)出了 3 個(gè) .nextevent咐旧,那么它會(huì)將后兩個(gè)(最近的兩個(gè))event 給緩存起來。此時(shí)如果有一個(gè) subscriber 訂閱了這個(gè) ReplaySubject绩蜻,那么這個(gè) subscriber 就會(huì)立即收到前面緩存的兩個(gè) .nextevent铣墨。
  3. 如果一個(gè) subscriber 訂閱已經(jīng)結(jié)束的 ReplaySubject,除了會(huì)收到緩存的 .nextevent 外办绝,還會(huì)收到那個(gè)終結(jié)的 .error 或者 .completeevent伊约。
  • (2)時(shí)序圖
    如下圖:最上面一條是 ReplaySubjectbufferSize 設(shè)為為 2)。
    下面兩條分別表示兩個(gè)新的訂閱孕蝉,它們訂閱的時(shí)間點(diǎn)不同屡律。可以發(fā)現(xiàn) ReplaySubject 的訂閱者一開始就能收到 ReplaySubject 之前發(fā)出的兩個(gè) Event(如果有的話)昔驱。
ReplaySubject
  • 實(shí)例 2.6.1.4 :
let disposeBag = DisposeBag()
 
//創(chuàng)建一個(gè)bufferSize為2的ReplaySubject
let subject = ReplaySubject<String>.create(bufferSize: 2)
 
//連續(xù)發(fā)送3個(gè)next事件
subject.onNext("111")
subject.onNext("222")
subject.onNext("333")
 
//第1次訂閱subject
subject.subscribe { event in
    print("第1次訂閱:", event)
}.disposed(by: disposeBag)
 
//再發(fā)送1個(gè)next事件
subject.onNext("444")
 
//第2次訂閱subject
subject.subscribe { event in
    print("第2次訂閱:", event)
}.disposed(by: disposeBag)
 
//讓subject結(jié)束
subject.onCompleted()
 
//第3次訂閱subject
subject.subscribe { event in
    print("第3次訂閱:", event)
}.disposed(by: disposeBag)

運(yùn)行結(jié)果:

image

2.6.2 Variables

  • 注意:由于 Variable 在之后版本中將被廢棄疹尾,建議使用 Varible 的地方都改用下面介紹的 BehaviorRelay 作為替代

2.6.2.1 Variable

-(1)基本介紹

  1. Variable 其實(shí)就是對(duì) BehaviorSubject 的封裝,所以它也必須要通過一個(gè)默認(rèn)的初始值進(jìn)行創(chuàng)建。
  2. Variable 具有 BehaviorSubject 的功能纳本,能夠向它的訂閱者發(fā)出上一個(gè) event 以及之后新創(chuàng)建的 event窍蓝。
  3. 不同的是,Variable 還把會(huì)把當(dāng)前發(fā)出的值保存為自己的狀態(tài)繁成。同時(shí)它會(huì)在銷毀時(shí)自動(dòng)發(fā)送 .completeevent吓笙,不需要也不能手動(dòng)給 Variables 發(fā)送 completed 或者 error 事件來結(jié)束它。
  4. 簡單地說就是 Variable 有一個(gè) value 屬性巾腕,我們改變這個(gè) value 屬性的值就相當(dāng)于調(diào)用一般 SubjectsonNext() 方法面睛,而這個(gè)最新的 onNext() 的值就被保存在 value 屬性里了,直到我們?cè)俅涡薷乃?/li>
  5. Variables 本身沒有 subscribe() 方法尊搬,但是所有 Subjects 都有一個(gè) asObservable() 方法叁鉴。我們可以使用這個(gè)方法返回這個(gè) VariableObservable 類型,拿到這個(gè) Observable 類型我們就能訂閱它了佛寿。
  • 實(shí)例 2.6.2.1
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    override func viewDidLoad() {
        super.viewDidLoad()
 
        let disposeBag = DisposeBag()
         
        //創(chuàng)建一個(gè)初始值為111的Variable
        let variable = Variable("111")
         
        //修改value值
        variable.value = "222"
         
        //第1次訂閱
        variable.asObservable().subscribe {
            print("第1次訂閱:", $0)
        }.disposed(by: disposeBag)
         
        //修改value值
        variable.value = "333"
         
        //第2次訂閱
        variable.asObservable().subscribe {
            print("第2次訂閱:", $0)
        }.disposed(by: disposeBag)
         
        //修改value值
        variable.value = "444"
    }
}

注意:由于 Variable 對(duì)象在 viewDidLoad() 方法內(nèi)初始化幌墓,所以它的生命周期就被限制在該方法內(nèi)。當(dāng)這個(gè)方法執(zhí)行完畢后冀泻,這個(gè) Variable 對(duì)象就會(huì)被銷毀常侣,同時(shí)它也就自動(dòng)地向它的所有訂閱者發(fā)出 .completed 事件

運(yùn)行結(jié)果:

image

2.6.2.2 BehaviorRelay

  • 1)基本介紹
  1. BehaviorRelay 是作為 Variable 的替代者出現(xiàn)的。它的本質(zhì)其實(shí)也是對(duì) BehaviorSubject 的封裝弹渔,所以它也必須要通過一個(gè)默認(rèn)的初始值進(jìn)行創(chuàng)建胳施。
  2. BehaviorRelay 具有 BehaviorSubject 的功能,能夠向它的訂閱者發(fā)出上一個(gè) event 以及之后新創(chuàng)建的 event肢专。
  3. BehaviorSubject 不同的是舞肆,不需要也不能手動(dòng)給 BehaviorReply 發(fā)送 completed 或者 error 事件來結(jié)束它(BehaviorRelay 會(huì)在銷毀時(shí)也不會(huì)自動(dòng)發(fā)送 .completeevent)。
  4. BehaviorRelay 有一個(gè) value 屬性博杖,我們通過這個(gè)屬性可以獲取最新值胆绊。而通過它的 accept() 方法可以對(duì)值進(jìn)行修改。
  • (2)上面的 Variable 樣例我們可以改用成 BehaviorRelay欧募,代碼如下:
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        let disposeBag = DisposeBag()
         
        //創(chuàng)建一個(gè)初始值為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")
    }
}

運(yùn)行結(jié)果:


image
  • (3)如果想將新值合并到原值上,可以通過 accept() 方法與 value 屬性配合來實(shí)現(xiàn)仆抵。(這個(gè)常用在表格上拉加載功能上跟继,BehaviorRelay 用來保存所有加載到的數(shù)據(jù))
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        let disposeBag = DisposeBag()
         
        //創(chuàng)建一個(gè)初始值為包含一個(gè)元素的數(shù)組的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"])
    }
}

運(yùn)行結(jié)果:

image

2.7 變換操作符:buffer、map镣丑、flatMap舔糖、scan等

  • 變換操作指的是對(duì)原始的 Observable 序列進(jìn)行一些轉(zhuǎn)換,類似于 Swift 中 CollectionType 的各種轉(zhuǎn)換莺匠。

2.7.1 buffer

  • (1)基本介紹

buffer 方法作用是緩沖組合金吗,第一個(gè)參數(shù)是緩沖時(shí)間,第二個(gè)參數(shù)是緩沖個(gè)數(shù),第三個(gè)參數(shù)是線程摇庙。
該方法簡單來說就是緩存 Observable 中發(fā)出的新元素旱物,當(dāng)元素達(dá)到某個(gè)數(shù)量,或者經(jīng)過了特定的時(shí)間卫袒,它就會(huì)將這個(gè)元素集合發(fā)送出來宵呛。

buffer
  • 實(shí)例 2.7.1
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
 
        let subject = PublishSubject<String>()
 
        //每緩存3個(gè)元素則組合起來一起發(fā)出。
        //如果1秒鐘內(nèi)不夠3個(gè)也會(huì)發(fā)出(有幾個(gè)發(fā)幾個(gè)夕凝,一個(gè)都沒有發(fā)空數(shù)組 [])
        subject
            .buffer(timeSpan: 1, count: 3, scheduler: MainScheduler.instance)
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
 
        subject.onNext("a")
        subject.onNext("b")
        subject.onNext("c")
         
        subject.onNext("1")
        subject.onNext("2")
        subject.onNext("3")
    }
}

運(yùn)行結(jié)果:

image

2.7.2 window

  • (1)基本介紹

window 操作符和 buffer 十分相似宝穗。不過 buffer 是周期性的將緩存的元素集合發(fā)送出來,而 window 周期性的將元素集合以 Observable 的形態(tài)發(fā)送出來码秉。
同時(shí) buffer 要等到元素搜集完畢后逮矛,才會(huì)發(fā)出元素序列。而 window 可以實(shí)時(shí)發(fā)出元素序列转砖。

window
  • 實(shí)例 2.7.2
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
         
        let subject = PublishSubject<String>()
         
        //每3個(gè)元素作為一個(gè)子Observable發(fā)出须鼎。
        subject
            .window(timeSpan: 1, count: 3, scheduler: MainScheduler.instance)
            .subscribe(onNext: { [weak self]  in
                print("subscribe: \($0)")
                $0.asObservable()
                    .subscribe(onNext: { print($0) })
                    .disposed(by: self!.disposeBag)
            })
            .disposed(by: disposeBag)
         
        subject.onNext("a")
        subject.onNext("b")
        subject.onNext("c")
         
        subject.onNext("1")
        subject.onNext("2")
        subject.onNext("3")
    }
}

運(yùn)行結(jié)果:

image

2.7.3 map

  • (1)基本介紹
    該操作符通過傳入一個(gè)函數(shù)閉包把原來的 Observable 序列轉(zhuǎn)變?yōu)橐粋€(gè)新的 Observable 序列。
map
  • 實(shí)例 2.7.3
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3)
    .map { $0 * 10}
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

運(yùn)行結(jié)果:
10
20
30

2.7.4 flatMap

  • (1)基本介紹
  1. map 在做轉(zhuǎn)換的時(shí)候容易出現(xiàn)“升維”的情況堪藐。即轉(zhuǎn)變之后莉兰,從一個(gè)序列變成了一個(gè)序列的序列。
  2. flatMap 操作符會(huì)對(duì)源 Observable 的每一個(gè)元素應(yīng)用一個(gè)轉(zhuǎn)換方法礁竞,將他們轉(zhuǎn)換成 Observables糖荒。 然后將這些 Observables 的元素合并之后再發(fā)送出來。即又將其 "拍扁"(降維)成一個(gè) Observable 序列模捂。
  3. 這個(gè)操作符是非常有用的捶朵。比如當(dāng) Observable 的元素本生擁有其他的 Observable 時(shí),我們可以將所有子 Observables 的元素發(fā)送出來狂男。
flatMap
  • 實(shí)例 2.7.4
let disposeBag = DisposeBag()
 
let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
 
let variable = Variable(subject1)
 
variable.asObservable()
    .flatMap { $0 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext("B")
variable.value = subject2
subject2.onNext("2")
subject1.onNext("C")

運(yùn)行結(jié)果:

image

2.7.5 flatMapLatest

  • (1)基本介紹
    flatMapLatest 與 flatMap 的唯一區(qū)別是:flatMapLatest 只會(huì)接收最新的 value 事件综看。
flatMapLatest
  • 實(shí)例2.7.5 : 這里我們將上例中的 flatMap 改為 flatMapLatest
let disposeBag = DisposeBag()
 
let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
 
let variable = Variable(subject1)
 
variable.asObservable()
    .flatMapLatest { $0 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext("B")
variable.value = subject2
subject2.onNext("2")
subject1.onNext("C")

運(yùn)行結(jié)果:

image

2.7.6 flatMapFirst

  • (1)基本介紹
  1. flatMapFirst 與 flatMapLatest 正好相反:flatMapFirst 只會(huì)接收最初的 value 事件。
  2. 該操作符可以防止重復(fù)請(qǐng)求:
    比如點(diǎn)擊一個(gè)按鈕發(fā)送一個(gè)請(qǐng)求岖食,當(dāng)該請(qǐng)求完成前红碑,該按鈕點(diǎn)擊都不應(yīng)該繼續(xù)發(fā)送請(qǐng)求。便可該使用 flatMapFirst 操作符泡垃。
flatMapFirst
  • 實(shí)例2.7.6 :這里我們將上例中的 flatMapLatest 改為 flatMapFirst析珊。
let disposeBag = DisposeBag()
 
let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
 
let variable = Variable(subject1)
 
variable.asObservable()
    .flatMapFirst { $0 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext("B")
variable.value = subject2
subject2.onNext("2")
subject1.onNext("C")

運(yùn)行結(jié)果:

image

2.7.7 concatMap

  • (1)基本介紹

concatMap 與 flatMap 的唯一區(qū)別是:當(dāng)前一個(gè) Observable 元素發(fā)送完畢后,后一個(gè)Observable 才可以開始發(fā)出元素蔑穴≈已埃或者說等待前一個(gè) Observable 產(chǎn)生完成事件后,才對(duì)后一個(gè) Observable 進(jìn)行訂閱存和。

concatMap
  • 實(shí)例2.7.7
let disposeBag = DisposeBag()
 
let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
 
let variable = Variable(subject1)
 
variable.asObservable()
    .concatMap { $0 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext("B")
variable.value = subject2
subject2.onNext("2")
subject1.onNext("C")
subject1.onCompleted() //只有前一個(gè)序列結(jié)束后奕剃,才能接收下一個(gè)序列

運(yùn)行結(jié)果:

image

2.7.8 scan

  • (1)基本介紹

scan 就是先給一個(gè)初始化的數(shù)衷旅,然后不斷的拿前一個(gè)結(jié)果和最新的值進(jìn)行處理操作。

scan
  • 實(shí)例2.7.8
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4, 5)
    .scan(0) { acum, elem in
        acum + elem
    }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

運(yùn)行結(jié)果

image

2.7.9 groupBy

  • (1)基本介紹

groupBy 操作符將源 Observable 分解為多個(gè)子 Observable纵朋,然后將這些子 Observable 發(fā)送出來柿顶。
也就是說該操作符會(huì)將元素通過某個(gè)鍵進(jìn)行分組,然后將分組后的元素序列以 Observable 的形態(tài)發(fā)送出來倡蝙。

groupBy
  • 實(shí)例2.7.9
let disposeBag = DisposeBag()
 
//將奇數(shù)偶數(shù)分成兩組
Observable<Int>.of(0, 1, 2, 3, 4, 5)
    .groupBy(keySelector: { (element) -> String in
        return element % 2 == 0 ? "偶數(shù)" : "基數(shù)"
    })
    .subscribe { (event) in
        switch event {
        case .next(let group):
            group.asObservable().subscribe({ (event) in
                print("key:\(group.key)    event:\(event)")
            })
            .disposed(by: disposeBag)
        default:
            print("")
        }
    }
.disposed(by: disposeBag)

運(yùn)行結(jié)果:

image

2.8 過濾操作符:filter九串、take、skip等

  • 過濾操作指的是從源 Observable 中選擇特定的數(shù)據(jù)發(fā)送寺鸥。

2.8.1 filter

  • (1)基本介紹

該操作符就是用來過濾掉某些不符合要求的事件猪钮。

filter
  • 實(shí)例2.8.1
let disposeBag = DisposeBag()
 
Observable.of(2, 30, 22, 5, 60, 3, 40 ,9)
    .filter {
        $0 > 10
    }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
image

2.8.2 distinctUntilChanged

  • 該操作符用于過濾掉連續(xù)重復(fù)的事件。


    distinctUntilChanged
  • 實(shí)例2.8.2
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 1, 1, 4)
    .distinctUntilChanged()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

運(yùn)行結(jié)果:

image

2.8.3 single

  • (1)基本介紹

限制只發(fā)送一次事件胆建,或者滿足條件的第一個(gè)事件烤低。
如果存在有多個(gè)事件或者沒有事件都會(huì)發(fā)出一個(gè) error 事件。
如果只有一個(gè)事件笆载,則不會(huì)發(fā)出 error 事件扑馁。

single
  • 實(shí)例2.8.3
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4)
    .single{ $0 == 2 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
Observable.of("A", "B", "C", "D")
    .single()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

運(yùn)行結(jié)果:

image

2.8.4 elementAt

  • 該方法實(shí)現(xiàn)只處理在指定位置的事件。


    elementAt
  • 實(shí)例2.8.4
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4)
    .elementAt(2)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

運(yùn)行結(jié)果:
3

2.8.5 ignoreElements

  • (1)基本介紹

該操作符可以忽略掉所有的元素凉驻,只發(fā)出 error 或 completed 事件腻要。
如果我們并不關(guān)心 Observable 的任何元素,只想知道 Observable 在什么時(shí)候終止涝登,那就可以使用 ignoreElements 操作符雄家。

ignoreElements
  • 實(shí)例2.8.5
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4)
    .ignoreElements()
    .subscribe{
        print($0)
    }
    .disposed(by: disposeBag)

運(yùn)行結(jié)果:
completed

2.8.6 take

  • 該方法實(shí)現(xiàn)僅發(fā)送 Observable 序列中的前 n 個(gè)事件,在滿足數(shù)量之后會(huì)自動(dòng) .completed胀滚。


    take
  • 實(shí)例2.8.6
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4)
    .take(2)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

運(yùn)行結(jié)果:
1
2

2.8.7 takeLast

  • 該方法實(shí)現(xiàn)僅發(fā)送 Observable 序列中的后 n 個(gè)事件趟济。


    takeLast
  • 實(shí)例2.8.7
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4)
    .takeLast(1)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

運(yùn)行結(jié)果:
4

2.8.8 skip

  • 該方法用于跳過源 Observable 序列發(fā)出的前 n 個(gè)事件。


    skip
  • 實(shí)例 2.8.8
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4)
    .skip(2)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

運(yùn)行結(jié)果:
3
4

2.8.9 Sample

  1. Sample 除了訂閱源 Observable 外咽笼,還可以監(jiān)視另外一個(gè) Observable顷编, 即 notifier 。
  2. 每當(dāng)收到 notifier 事件剑刑,就會(huì)從源序列取一個(gè)最新的事件并發(fā)送媳纬。而如果兩次 notifier 事件之間沒有源序列的事件,則不發(fā)送值施掏。
Sample
  • 實(shí)例2.8.9
let disposeBag = DisposeBag()
 
let source = PublishSubject<Int>()
let notifier = PublishSubject<String>()
 
source
    .sample(notifier)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
source.onNext(1)
 
//讓源序列接收接收消息
notifier.onNext("A")
 
source.onNext(2)
 
//讓源序列接收接收消息
notifier.onNext("B")
notifier.onNext("C")
 
source.onNext(3)
source.onNext(4)
 
//讓源序列接收接收消息
notifier.onNext("D")
 
source.onNext(5)
 
//讓源序列接收接收消息
notifier.onCompleted()

運(yùn)行結(jié)果:

1
2
4
5

2.8.10 debounce

  • (1)基本介紹
  1. debounce 操作符可以用來過濾掉高頻產(chǎn)生的元素层宫,它只會(huì)發(fā)出這種元素:該元素產(chǎn)生后,一段時(shí)間內(nèi)沒有新元素產(chǎn)生其监。
  2. 換句話說就是,隊(duì)列中的元素如果和下一個(gè)元素的間隔小于了指定的時(shí)間間隔限匣,那么這個(gè)元素將被過濾掉抖苦。
  3. debounce 常用在用戶輸入的時(shí)候毁菱,不需要每個(gè)字母敲進(jìn)去都發(fā)送一個(gè)事件,而是稍等一下取最后一個(gè)事件锌历。
debounce
  • 實(shí)例2.8.10:
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
 
        //定義好每個(gè)事件里的值以及發(fā)送的時(shí)間
        let times = [
            [ "value": 1, "time": 0.1 ],
            [ "value": 2, "time": 1.1 ],
            [ "value": 3, "time": 1.2 ],
            [ "value": 4, "time": 1.2 ],
            [ "value": 5, "time": 1.4 ],
            [ "value": 6, "time": 2.1 ]
        ]
         
        //生成對(duì)應(yīng)的 Observable 序列并訂閱
        Observable.from(times)
            .flatMap { item in
                return Observable.of(Int(item["value"]!))
                    .delaySubscription(Double(item["time"]!),
                                       scheduler: MainScheduler.instance)
            }
            .debounce(0.5, scheduler: MainScheduler.instance) //只發(fā)出與下一個(gè)間隔超過0.5秒的元素
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
    }
}

運(yùn)行結(jié)果:
1
5
6

2.9 條件和布爾操作符:amb贮庞、takeWhile、skipWhile等

  • 條件和布爾操作會(huì)根據(jù)條件發(fā)射或變換 Observables究西,或者對(duì)他們做布爾運(yùn)算窗慎。

2.9.1 amb

  • 當(dāng)傳入多個(gè) Observablesamb 操作符時(shí),它將取第一個(gè)發(fā)出元素或產(chǎn)生事件的 Observable卤材,然后只發(fā)出它的元素遮斥。并忽略掉其他的 Observables

    amb

  • 實(shí)例 2.9.1

let disposeBag = DisposeBag()
 
let subject1 = PublishSubject<Int>()
let subject2 = PublishSubject<Int>()
let subject3 = PublishSubject<Int>()
 
subject1
    .amb(subject2)
    .amb(subject3)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject2.onNext(1)
subject1.onNext(20)
subject2.onNext(2)
subject1.onNext(40)
subject3.onNext(0)
subject2.onNext(3)
subject1.onNext(60)
subject3.onNext(0)
subject3.onNext(0)

運(yùn)行結(jié)果:
1
2
3

2.9.2 takeWhile

  • 該方法依次判斷 Observable 序列的每一個(gè)值是否滿足給定的條件扇丛。 當(dāng)?shù)谝粋€(gè)不滿足條件的值出現(xiàn)時(shí)术吗,它便自動(dòng)完成。


    takeWhile
  • 實(shí)例 2.9.2

let disposeBag = DisposeBag()
 
Observable.of(2, 3, 4, 5, 6)
    .takeWhile { $0 < 4 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

運(yùn)行結(jié)果:
2
3

2.9.3 takeUntil

  • 基本介紹
  1. 除了訂閱源 Observable 外帆精,通過 takeUntil 方法我們還可以監(jiān)視另外一個(gè) Observable较屿, 即 notifier
  2. 如果 notifier 發(fā)出值或 complete 通知卓练,那么源 Observable 便自動(dòng)完成隘蝎,停止發(fā)送事件。
    takeUntil
  • 實(shí)例 2.9.3
let disposeBag = DisposeBag()
 
let source = PublishSubject<String>()
let notifier = PublishSubject<String>()
 
source
    .takeUntil(notifier)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
source.onNext("a")
source.onNext("b")
source.onNext("c")
source.onNext("d")
 
//停止接收消息
notifier.onNext("z")
 
source.onNext("e")
source.onNext("f")
source.onNext("g")

輸出結(jié)果:
a
b
c
d

2.9.4 skipWhile

  • 基本介紹
  1. 該方法用于跳過前面所有滿足條件的事件襟企。
  2. 一旦遇到不滿足條件的事件嘱么,之后就不會(huì)再跳過了。


    skipWhile
  • 實(shí)例 2.9.4
let disposeBag = DisposeBag()
 
Observable.of(2, 3, 4, 5, 6)
    .skipWhile { $0 < 4 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
    }
}

運(yùn)行結(jié)果:
4
5
6

2.9.5 skipUntil

  • 基本介紹
  1. 同上面的 takeUntil 一樣整吆,skipUntil 除了訂閱源 Observable 外拱撵,通過 skipUntil 方法我們還可以監(jiān)視另外一個(gè) Observable, 即 notifier 表蝙。
  2. takeUntil 相反的是拴测。源 Observable 序列事件默認(rèn)會(huì)一直跳過,直到 notifier 發(fā)出值或 complete 通知府蛇。
skipUntil
  • 實(shí)例 2.9.5
let disposeBag = DisposeBag()
 
let source = PublishSubject<Int>()
let notifier = PublishSubject<Int>()
 
source
    .skipUntil(notifier)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
source.onNext(1)
source.onNext(2)
source.onNext(3)
source.onNext(4)
source.onNext(5)
 
//開始接收消息
notifier.onNext(0)
 
source.onNext(6)
source.onNext(7)
source.onNext(8)
 
//仍然接收消息
notifier.onNext(0)
 
source.onNext(9)

運(yùn)行結(jié)果:
6
7
8
9

2.10 結(jié)合操作符:startWith集索、merge、zip等

2.10.1 startWith

  • 基本介紹:

該方法會(huì)在 Observable 序列開始之前插入一些事件元素汇跨。即發(fā)出事件消息之前务荆,會(huì)先發(fā)出這些預(yù)先插入的事件消息

  • 圖解:


    startWiith
  • 實(shí)例 2.10.1
let disposeBag = DisposeBag()
         
Observable.of("2", "3")
    .startWith("1")
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

運(yùn)行結(jié)果:

1
2
3

  • 實(shí)例2.10.1.1: 可以插入多個(gè)數(shù)據(jù)
let disposeBag = DisposeBag()
 
Observable.of("2", "3")
    .startWith("a")
    .startWith("b")
    .startWith("c")
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

運(yùn)行結(jié)果:

c
b
a
2
3

2.10.2 merge

  • 基本介紹:

該方法可以將多個(gè)(兩個(gè)或兩個(gè)以上的)Observable 序列合并成一個(gè) Observable 序列。

  • 圖解:


    merge
  • 實(shí)例 2.10.1
let disposeBag = DisposeBag()
         
let subject1 = PublishSubject<Int>()
let subject2 = PublishSubject<Int>()
 
Observable.of(subject1, subject2)
    .merge()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext(20)
subject1.onNext(40)
subject1.onNext(60)
subject2.onNext(1)
subject1.onNext(80)
subject1.onNext(100)
subject2.onNext(1)

運(yùn)行結(jié)果:

20
40
60
1
80
100
1

2.10.3 zip

  • 基本介紹:

該方法可以將多個(gè)(兩個(gè)或兩個(gè)以上的)Observable 序列壓縮成一個(gè) Observable 序列穷遂。
而且它會(huì)等到每個(gè) Observable 事件一一對(duì)應(yīng)地湊齊之后再合并函匕。

  • 圖解:


    zip
  • 實(shí)例 2.10.1
let disposeBag = DisposeBag()
         
let subject1 = PublishSubject<Int>()
let subject2 = PublishSubject<String>()
 
Observable.zip(subject1, subject2) {
    "\($0)\($1)"
    }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext(1)
subject2.onNext("A")
subject1.onNext(2)
subject2.onNext("B")
subject2.onNext("C")
subject2.onNext("D")
subject1.onNext(3)
subject1.onNext(4)
subject1.onNext(5)

運(yùn)行結(jié)果:

1A
2B
3C
4D

  • zip 常常用在整合網(wǎng)絡(luò)請(qǐng)求上:

比如我們想同時(shí)發(fā)送兩個(gè)請(qǐng)求,只有當(dāng)兩個(gè)請(qǐng)求都成功后蚪黑,再將兩者的結(jié)果整合起來繼續(xù)往下處理盅惜。這個(gè)功能就可以通過 zip 來實(shí)現(xiàn)中剩。

//第一個(gè)請(qǐng)求
let userRequest: Observable<User> = API.getUser("me")
 
//第二個(gè)請(qǐng)求
let friendsRequest: Observable<Friends> = API.getFriends("me")
 
//將兩個(gè)請(qǐng)求合并處理
Observable.zip(userRequest, friendsRequest) {
        user, friends in
        //將兩個(gè)信號(hào)合并成一個(gè)信號(hào),并壓縮成一個(gè)元組返回(兩個(gè)信號(hào)均成功)
        return (user, friends)
    }
    .observeOn(MainScheduler.instance) //加這個(gè)是應(yīng)為請(qǐng)求在后臺(tái)線程抒寂,下面的綁定在前臺(tái)線程结啼。
    .subscribe(onNext: { (user, friends) in
        //將數(shù)據(jù)綁定到界面上
        //.......
    })
    .disposed(by: disposeBag)

2.10.4 combineLatest

  • 基本介紹:
  1. 該方法同樣是將多個(gè)(兩個(gè)或兩個(gè)以上的)Observable 序列元素進(jìn)行合并。
  2. 但與 zip 不同的是屈芜,每當(dāng)任意一個(gè) Observable 有新的事件發(fā)出時(shí)郊愧,它會(huì)將每個(gè) Observable 序列的最新的一個(gè)事件元素進(jìn)行合并。
  • 圖解:


    combineLatest
  • 實(shí)例 2.10.1
let disposeBag = DisposeBag()
         
let subject1 = PublishSubject<Int>()
let subject2 = PublishSubject<String>()
 
Observable.combineLatest(subject1, subject2) {
    "\($0)\($1)"
    }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext(1)
subject2.onNext("A")
subject1.onNext(2)
subject2.onNext("B")
subject2.onNext("C")
subject2.onNext("D")
subject1.onNext(3)
subject1.onNext(4)
subject1.onNext(5)

運(yùn)行結(jié)果:


image

2.10.5 withLatestFrom

  • 基本介紹:

該方法將兩個(gè) Observable 序列合并為一個(gè)井佑。每當(dāng) self 隊(duì)列發(fā)射一個(gè)元素時(shí)属铁,便從第二個(gè)序列中取出最新的一個(gè)值。

  • 圖解:

  • 實(shí)例 2.10.1

let disposeBag = DisposeBag()
 
let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()
 
subject1.withLatestFrom(subject2)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext("A")
subject2.onNext("1")
subject1.onNext("B")
subject1.onNext("C")
subject2.onNext("2")
subject1.onNext("D")

運(yùn)行結(jié)果:

1
1
2

2.10.6 switchLatest

  • 基本介紹:
  1. switchLatest 有點(diǎn)像其他語言的 switch 方法毅糟,可以對(duì)事件流進(jìn)行轉(zhuǎn)換红选。
  2. 比如本來監(jiān)聽的 subject1,我可以通過更改 variable 里面的 value 更換事件源姆另。變成監(jiān)聽 subject2喇肋。
  • 圖解:


    switchLatest
  • 實(shí)例 2.10.1
let disposeBag = DisposeBag()
 
let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
 
let variable = Variable(subject1)
 
variable.asObservable()
    .switchLatest()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext("B")
subject1.onNext("C")
 
//改變事件源
variable.value = subject2
subject1.onNext("D")
subject2.onNext("2")
 
//改變事件源
variable.value = subject1
subject2.onNext("3")
subject1.onNext("E")

運(yùn)行結(jié)果:


image

2.11

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市迹辐,隨后出現(xiàn)的幾起案子蝶防,更是在濱河造成了極大的恐慌,老刑警劉巖明吩,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件间学,死亡現(xiàn)場(chǎng)離奇詭異救拉,居然都是意外死亡蔼囊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門州弟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仍律,“玉大人嘿悬,你說我怎么就攤上這事∷” “怎么了善涨?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長草则。 經(jīng)常有香客問我钢拧,道長,這世上最難降的妖魔是什么炕横? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任源内,我火速辦了婚禮,結(jié)果婚禮上份殿,老公的妹妹穿的比我還像新娘姿锭。我一直安慰自己塔鳍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布呻此。 她就那樣靜靜地躺著,像睡著了一般腔寡。 火紅的嫁衣襯著肌膚如雪焚鲜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天放前,我揣著相機(jī)與錄音忿磅,去河邊找鬼。 笑死凭语,一個(gè)胖子當(dāng)著我的面吹牛葱她,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播似扔,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼吨些,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了炒辉?” 一聲冷哼從身側(cè)響起豪墅,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎黔寇,沒想到半個(gè)月后偶器,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缝裤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年屏轰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憋飞。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡霎苗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搀崭,到底是詐尸還是另有隱情叨粘,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布瘤睹,位于F島的核電站升敲,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏轰传。R本人自食惡果不足惜驴党,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望获茬。 院中可真熱鬧港庄,春花似錦倔既、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至把还,卻和暖如春实蓬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吊履。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國打工安皱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人艇炎。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓酌伊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缀踪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子居砖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

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