RxSwift之路 1#Swift語法知識準備

沒故事的卓同學發表於2017-07-11

在開始學習 RxSwift 之前,一定要對 Swift 相關語法有所瞭解,否則就很難理解為什麼可以這樣。關於 Swift 的學習其實只要看看 Swift 的官方文件就可夠了。我之前也列過一些學習資源:來自一線開發者的Swift學習資源推薦
現在開始進入正題。

Swift的優勢

想一個有趣的問題,為什麼沒有 RxObjc 呢?
實際上響應式的程式設計框架對語言還是有些要求的,當然 OC 確實也有一個奠基式的 FRP 框架 ReactiveCocoa。但是客觀的說,在 Swift 裡響應式的框架寫起來會愉快的多,或者說更能發揮出語言的優勢。
Swift 契合響應式有以下幾點:

  • 強型別,強型別的泛型
  • 靈活的閉包
  • 對併發模型的原生支援(Swift 5 的特性,還未釋出

Enum的關聯值和泛型

Swift 中的列舉(Enum)能力相比 OC 可以說得到了昇華。不再只是一個類似預編譯時的巨集,而是一個完整的型別。和 Struct 一樣,可以給他定義初始化方法,宣告函式,新增擴充套件。同樣的泛型同樣也試用於 Enum。
列舉還有一項神奇的能力叫關聯值。一個列舉可以的值可以是不同的型別。官方手冊裡的示例程式碼如下:

enum ServerResponse {
    case result(String, String)
    case failure(String)
}

let success = ServerResponse.result("6:00 am", "8:09 pm")
let failure = ServerResponse.failure("Out of cheese.")

switch success {
case let .result(sunrise, sunset):
    print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .failure(message):
    print("Failure...  \(message)")
}複製程式碼

每個 case 可以攜帶不同的型別,並且可以不止是一個值。
當 Enum 結合泛型,就發生了有趣的事。直接貼程式碼:

enum OptionalValue<Wrapped> {
    case none
    case some(Wrapped)
}

var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)複製程式碼

這就是 Swift 中的 Optional 實現的類似程式碼。使用列舉實現,表示值有兩種可能:沒有值的 .none 和是 Wrapped 型別的 .some。
有了以上的知識我們再來看 Rx 中的事件流中的值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
}複製程式碼

每一次事件的值有三種可能:1.值(next),2.完成結束(completed),3.失敗(error)。

函式的預設引數

如果函式在宣告時設定了一個預設值,那麼在呼叫時這個引數就可以不傳。
假設我們給 Int 定義個擴充套件方法increment。如果不傳入值則預設加1,如果傳入就按照傳入的值增加:

extension Int {
    func increment(with number: Int = 1) -> Int {
        return self + number
    }
}複製程式碼

使用的時候 Xcode 就會提示兩個函式:


不再需要像 OC 一樣定義幾個函式。
Rx 表示訂閱的subscribe函式,有時只要寫onNext,有時只要寫onError,就是因為這個函式在宣告時同時指定了預設引數:

extension ObservableType {

   public func subscribe(file: String = #file,line: UInt = #line,
                                          function: String = #function,
                                          onNext: ((E) -> Void)? = nil,
                                          onError: ((Swift.Error) -> Void)? = nil, 
                                          onCompleted: (() -> Void)? = nil,
                                          onDisposed: (() -> Void)? = nil)-> Disposable {
     // ...
    }複製程式碼

可以看到這個函式為訂閱每個事件可能都宣告瞭預設引數,所以你在訂閱時可以只訂閱自己關注的訂閱。

閉包

閉包的使用類似 OC 中的 block,具體使用就不再介紹。提一下新手很容易忽略的語法糖。

閉包簡化語法

  • 閉包引數、返回值型別自動推斷
    因為 Swift 是強型別語言,閉包的引數的型別可以推斷出來。比如如下程式碼:
    reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
      return s1 > s2
    })複製程式碼
    s1 和 s2 的型別和返回值型別 Xcode 可以推斷出來可以省略:
    reversedNames = names.sorted(by: { (s1, s2)  in
      return s1 > s2
    })複製程式碼
  • 閉包表示式只有一行,可以省略return
    因為只有一行,表示式執行的結果肯定就是要返回的值,所以可以省略。接上面的程式碼可以優化成這樣:
    reversedNames = names.sorted(by: { 
             (s1, s2)  in   s1 > s2
    })複製程式碼
  • 用$N代替引數
    有時實參的引數名沒什麼用,可以直接用$N代表實參,$0代表第一個引數。上面的程式碼進行這樣的簡化後就寫成了這樣:
    reversedNames = names.sorted(by:  { $0 > $1 })複製程式碼
  • 二元運算時可以直接省略只用操作符表示
    當表示式只有一行時,二元運算的表示式可以直接省略到只剩一個運算子,上面的可以省略為:
    reversedNames = names.sorted(by:  >)複製程式碼

尾閉包

當引數的最好一個引數是閉包時,可以直接把最後一個閉包的實現跟在函式後面。
直接貼程式碼表示:

// 一個最後一個引數是閉包的函式
func someFunctionThatTakesAClosure(closure: () -> Void) {
    // function body goes here
}

// 預設的呼叫方法
someFunctionThatTakesAClosure(closure: {
    // closure's body goes here
})

// 省略最後一個引數的方法名,並且把閉包的實現直接跟在函式後面
someFunctionThatTakesAClosure() {
    // trailing closure's body goes here
}複製程式碼

這種寫法簡化了程式碼,讓程式碼看起來更簡潔。

Rx 中的資料流操作符可以靈活的組織閉包,經常會用到簡化的閉包的語法。

  Observable.of(1, 2, 3, 4, 5, 6)
    .filter { $0 % 2 == 0}
    .subscribe(onNext: {
      print($0)
})複製程式碼

剛開始的時候可能有點看不明白,慢慢的還原閉包的語法,之後看多了就會熟悉的。

歡迎關注我的微博:@沒故事的卓同學

相關文章