RxSwift 之變換操作

BigNerdCoding發表於2017-10-05

可能剛開始接觸 RxSwift 時候,你會覺得 RxSwift 非常難懂也不容易學。其實學習每一門新知識或多或少都會出現這種情況。不過我相信認知讀了前幾篇文章後,你會深感 RxSwift 的強大。它在簡化程式碼量的同時也讓整個程式的邏輯變的更為清晰。

這篇文章將會繼續介紹另一組非常重要的 RxSwift 操作:Transforming Operator 。通過這組操作我們可以在觀察者使用之前對資料進行一次預處理。

Transforming elements

可觀察物件每次都只會傳送一個資料,但是有時候訂閱者卻希望獲得是一組資料。其中最典型的應用場景就是 UITableView 這類資料來源為陣列的檢視。RxSwift 中實現此功能最簡單的方法就是通過 toArray 操作。如下圖所示, toArray 操作能將可觀察示例傳送的資料組合為陣列物件從而簡化訂閱者的後續處理。

01
01

let strikes = PublishSubject<String>()
let disposeBag = DisposeBag()

strikes
    .toArray()
    .subscribe(onNext: {
        print($0)
    })
    .addDisposableTo(disposeBag)

strikes.onNext("1")
strikes.onNext("2")
strikes.onNext("3")

strikes.onCompleted()複製程式碼

除了將元素轉化為陣列之外,我們還可以使用函數語言程式設計方法對每個資料進行變換操作。其中最常見的就是 map 以及變種 mapWithIndex 。這裡我們先來看 map 操作的演示示例:

02
02

通過 map 我們將內部資料數值成功擴大了一倍,而圖示對應程式碼為:

let strikes = PublishSubject<Int>()

let disposeBag = DisposeBag()

strikes
    .map {
        $0 * 2
    }
    .subscribe(onNext: {
        print($0)
    })
    .addDisposableTo(disposeBag)

strikes.onNext(1)
strikes.onNext(2)
strikes.onNext(3)

strikes.onCompleted()複製程式碼

與之前介紹的其它變種操作一樣, mapWithIndex 也只是在 map 基礎上新增了索引引數。下圖中演示了只對索引大於 1 的 Double 操作:

03
03

對應程式碼如下:

let strikes = PublishSubject<Int>()

let disposeBag = DisposeBag()

strikes
    .mapWithIndex { integer, index in
        index > 1 ? integer * 2 : integer 
    }
    .subscribe(onNext: {
        print($0)
    })
    .addDisposableTo(disposeBag)

strikes.onNext(1)
strikes.onNext(2)
strikes.onNext(3)

strikes.onCompleted()複製程式碼

Transforming inner observables

並不是每個型別物件都會被設計為可觀察型別,有時候可能只是物件例項的某個屬性是可觀察型別。例如,下面的 Student 型別:

struct Student {
    var score: Variable<Int>
}複製程式碼

Student 型別本身並不是可觀察型別,但是屬性 score 卻屬於 Variable 型別。對於這種情況我們可以通過 flatMap 系列操作對 score 進行觀察從而將 Student 型別例項轉化為某種意義上的可觀察型別。

第一個基本操作就是 flatMap,在 Swift 標準庫裡該操作用於函數語言程式設計,它能將那些多維集合型別轉換為一維集合型別。而 flatMap 在 RxSwift 中的操作過程如下圖:

04
04

圖中第一行 O1、O2、O3 表示三個型別例項,然後該型別例項有一個可觀察屬性 value 。通過 flatMap 操作我們將其轉成了最後一行的可觀察序列。剛開始 O1、O2、O3value 值分別為 1、2、3,然後經過 flatMap 操作轉化為了 10、20、30 。然後我們修改 O1、O2value 為 4 和 5,此時 flatMap 會獲得通知並生成數值 40、50。

將這段操作應用到 Student 型別上的程式碼:

let disposeBag = DisposeBag()

// 1
let o1  = Student(score: Variable(1))
let o2 = Student(score: Variable(2))
let o3 = Student(score: Variable(3))

// 2
let student = PublishSubject<Student>()

// 3
student.asObservable()
    .flatMap {
        $0.score.asObservable()
    }
    .map {
        $0 * 10
    }
    .subscribe(onNext: {
        print($0)
    })
    .addDisposableTo(disposeBag)

student.onNext(o1)
student.onNext(o2)
student.onNext(o3)

o1.score.value = 4

o2.score.value = 5

student.onCompleted()複製程式碼

與之前一樣 flatMap 也有一個名為 flatMapWithIndex 的變種操作,讀者可以自己查閱文件。接下來,我會介紹 flatMap 系列的另兩個操作 flatMapLatestflatMapFirst

flatMap 不同,flatMapLatest 只會關注最新的可觀察物件,而前者則對所有的可觀察物件作出響應。你可以將 flatMapLatest 看作是 map 以及 switchLatest 的組合操作。下圖展示了 flatMapLatest 的具體操作:

05
05

可以看到 flatMapLatest 並不會和之前一樣在最下面一行產生 30、50 。因為 O1 數值設為 3 的時候,最新的可觀察物件是 O2 ,同理設定 O2 數值時最新物件已經是 O3 了,而 flatMapLatest 對於這些行為不會做出任何響應。

操作過程的對應程式碼如下:

let disposeBag = DisposeBag()

// 1
let o1  = Student(score: Variable(1))
let o2 = Student(score: Variable(2))
let o3 = Student(score: Variable(4))

// 2
let student = PublishSubject<Student>()

// 3
student.asObservable()
    .flatMapLatest {
        $0.score.asObservable()
    }
    .map {
        $0 * 10
    }
    .subscribe(onNext: {
        print($0)
    })
    .addDisposableTo(disposeBag)

student.onNext(o1)
student.onNext(o2)

o1.score.value = 3

student.onNext(o3)

o2.score.value = 5

o3.score.value = 6

student.onCompleted()複製程式碼

flatMapFirst 操作相對來說更簡單,它只會對第一個可觀察物件做出響應。也就是說,在與上例一樣的情形下它只會對 o1 做出響應。

let disposeBag = DisposeBag()

// 1
let o1  = Student(score: Variable(1))
let o2 = Student(score: Variable(2))
let o3 = Student(score: Variable(4))

// 2
let student = PublishSubject<Student>()

// 3
student.asObservable()
    .flatMapLatest {
        $0.score.asObservable()
    }
    .map {
        $0 * 10
    }
    .subscribe(onNext: {
        print($0)
    })
    .addDisposableTo(disposeBag)

student.onNext(o1)
student.onNext(o2)

o1.score.value = 3

student.onNext(o3)

o2.score.value = 5

o3.score.value = 6

student.onCompleted()

/* 列印結果
10
30
*/複製程式碼

##總結
本文簡單了介紹了 RxSwift 中一些常見的變換操作。雖然文中的程式碼和示例有些簡單,但這並不影響理解。

原文地址

相關文章