RxSwift使用教程

轉自: http://blog.csdn.net/Hello_Hwc/article/details/51859330


前言

RxSwiftSwift函數響應式編程的一個開源庫,由Github的ReactiveX組織開發,維護。

RxSwift的目的是讓讓數據/事件流和異步任務能夠更方便的序列化處理,能夠使用Swift進行響應式編程

目前,RxSwift在Github上收到了5000+Star,600+fork。

本文的目的

  • 介紹RxSwift的核心思想
  • 講解RxSwift的基礎使用
  • 介紹RxSwift的優點

如果你有時間,建議先讀讀RxSwift的文檔,這會給你一個最基本的認識

本文不會講解函數式編程,也不會講解函數響應式編程的概念,計劃後面單獨出一篇博客來講解Swift與函數式編程。

本文來自於官方文檔的翻譯,官方example代碼的閱讀,以及自己的理解


RxSwift和ReativeCocoa

老一點的iOS開發者應該對ReativeCocoa有一些瞭解,iOS響應式編程的鼻祖。就個人來看

  • ReativeCocoa更適合OC,缺點語法複雜,概念繁多,參考資料少(尤其RAC4),不易理解
  • RxSwift對Swift的兼容很好,利用了很多的Swift特性,語法簡單,概念清楚

So,個人是非常推薦RxSwift的


Observables/Sequences

先複習下SequenceType。這是Swift中的一個協議,比如Swift中的Array就遵循這個協議,通過這個協議,你可以這樣的去操作一個Array

let array = [1,2,3,4,5]
let array2 = array.filter({$0 > 1}).map({$0 * 2})//4 6 8 10
var indexGenerator = array2.generate()
let fisrt = indexGenerator.next() // 4
let seoncd = indexGenerator.next() //6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

也就是說,把Array作爲一個序列,然後依次對這個序列進行過濾,映射等操作,也可以通過indexGenerator來一個個的獲取序列中的數據。

RxSwift的核心思想和這個類似。

RxSwift的核心是想是 Observable<Element> sequence,Observable表示可監聽或者可觀察,也就是說RxSwift的核心思想是可監聽的序列。並且,Observable sequence可以接受異步信號,也就是說,信號是可以異步給監聽者的

  • Observable(ObservableType) 和 SequenceType類似
  • ObservableType.subscribe 和 SequenceType.generate類似
  • 由於RxSwift支持異步獲得信號,所以用ObservableType.subscribe,這和indexGenerator.next()類似

本文把RxSwift中的序列的每一個Element成爲信號,因爲異步的Element是與時間相關的,稱作信號更好理解一點

RxSwift中,ObservableType.subscribe的回調(新的信號到來)一共有三種

enum Event<Element>  {
    case Next(Element)      // 新的信號到來
    case Error(ErrorType)   // 信號發生錯誤,序列不會再產生信號
    case Completed          // 序列發送信號完成,不會再產生新的信號
}
protocol ObserverType {
    func on(event: Event<Element>) //監聽所有的信號
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

取消監聽

Observable分爲兩種

  1. 在有限的時間內會自動結束(Completed/Error),比如一個網絡請求當作一個序列,當網絡請求完成的時候,Observable自動結束,資源會被釋放
  2. 信號不會自己結束,最簡單的比如一個Timer,每隔一段時間發送一個新的信號過來,這時候需要手動取消監聽,來釋放相應的資源,又比如一個label.rac_text是一個Obserable,通常需要這樣調用addDisposableTo(disposeBag)來讓其在deinit,也就是所有者要釋放的時候,自動取消監聽。
class Observable<Element> {
    func subscribe(observer: Observer<Element>) -> Disposable //調用Disposable的方法來取消
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

當然,除了手動釋放,RxSwift提供了一些操作符,比如 takeUntil來根據條件取消

sequence
    .takeUntil(self.rx_deallocated) //當對象要釋放的時候,取消監聽
    .subscribe {
        print($0)
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

信號處理的順序

Observable有個隱式的約定,那就是在一個信號處理完成之前,不會發送下一個信號,不管發送信號的線程是併發的or串行的。

比如

someObservable
  .subscribe { (e: Event<Element>) in
      print("Event processing started")
      // processing
      print("Event processing ended")
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

只會出現

Event processing started
Event processing ended
Event processing started
Event processing ended
Event processing started
Event processing ended
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

不會出現

Event processing started
Event processing started
Event processing ended
Event processing ended
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

第一個例子

我們監聽textfield的文字變化,然後,Log出text,當button點擊的時候,取消這次監聽

class ObservableAndCancelController : UIViewController{
    var subscription:Disposable?

    @IBOutlet weak var textfield: UITextField!
    @IBAction func cancelObserve(sender: AnyObject) {
        subscription?.dispose()
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        subscription = textfield.rx_text.subscribeNext { (text) in
            print(text)
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

RxSwift用extensiton的方式,爲UITextfield,UIlabel等控件添加了很多可監聽的屬性,這裏的textfield.rx_text就是一個

效果:隨着文字輸入,實時Log出textfield的文字,當點擊button之後,再輸入,則不會Log


操作符(Operators)

在上文的第一個例子裏面,你看到了監聽信號,並且log出值。事實上,這樣直接處理信號的時候是很少的,很多時候,我們需要對信號進行映射,過濾,這時候我們就要用到操作符了。在這個文檔裏,你可以找到所有的操作符。

關於操作符效果,你可以參見http://rxmarbles.com/的可視化效果,這會給你一個更好的理解

例子二,map,filter,combineLatest

  • map 對信號(Element)進行映射處理。比如輸入是String,影射到Bool
  • filter 對信號(Element)進行過濾處理。返回信號,和輸入的信號是同一種類型
  • combineLatest 對兩種信號的值進行結合。可以返回不同種類的信號。

例如

     let firstObserverable = firstTextfield.rx_text.map({"first" + $0})
     let secondObserverable = secondTextfield.rx_text.filter({$0.characters.count > 3})
      _ =  Observable.combineLatest(firstObserverable, secondObserverable, resultSelector:{ ($0 + $1,$0.characters.count + $1.characters.count)}).subscribeNext { (element) in
            print("combineLatest:\(element)")
      }        
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

對於,每一個fistTextfield的信號,在字符串開始處增加”first”;對secondTextfield的信號進行過濾,當長度大於3的時候,纔會繼續傳遞。對兩個信號進行結合,取truple類型,然後打印出來。

所以,當我在fistTextfield中,輸入1234,然後secondTextfield中依次輸入abcdefg的時候

combineLatest:("first1234abcd", 13)
combineLatest:("first1234abcd3", 14)
combineLatest:("first1234abcd", 13)
combineLatest:("first1234abcde", 14)
combineLatest:("first1234abcdef", 15)
combineLatest:("first1234abcdefg", 16)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

例子三,創建一個Observable

Observerable可以用來處理任務,並且異步返回Event信號(Next,Error,Completion)

比如,這樣一個方法

//Observable就是處理輸入,並且把description發送出去
func createObserveable(object:AnyObject?)->Observable<String?>{
    return Observable.create({ observer in
        observer.onNext(object?.description)
        observer.onCompleted()
        return NopDisposable.instance
    })
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

這樣調用

_ = createObserveable(test).subscribe({ (event) in
      switch event{
      case .Next(let value):
          print(value)
      case .Completed:
          print("Completed")
      case .Error(let error):
          print(error)
      }
  })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

然後,Log如下

Optional("{\n    a = b;\n    1 = 2;\n}")
Completed
  • 1
  • 2
  • 1
  • 2

可以看到,創建一個Observable相當容易,調用Observable.create,在必要的時候發送onNext,onError,onCompleted信號。然後返回一個Disposable用來取消信號

throttle/retry/distinctUntilChanged/flatMapLatest

  • throttle 忽略上一個信號的一段時間的變化,也就是說一段時間沒有新的信號輸入,纔會向下發送
  • distinctUntilChanged 直到信號改變了再發送
  • retry 如果失敗,重新嘗試的次數
  • flatMapLatest 僅僅執行最新的信號,當有新的信號來的時候,取消上一次未執行完的整個序列

最直接的例子就是搜索,通常我們想要

  1. 用戶用一段時間沒有輸入的時候,在進進行網絡請求,不然網絡請求太頻繁,對客戶端和服務器都是負擔
  2. 當新的請求來的時候,如果上一個未完成,則取消上一個
  3. 如果網絡失敗,能重新請求幾次就更好了

這時候,用RxSwift你得代碼會變的非常簡單

let searchResults = searchBar.rx_text
    .throttle(0.3, scheduler: MainScheduler.instance)
    .distinctUntilChanged()
    .flatMapLatest { query -> Observable<[Repository]> in
        if query.isEmpty {
            return Observable.just([])
        }

        return doSearchAPI(query).retry(3)
            .catchErrorJustReturn([])
    }
    .observeOn(MainScheduler.instance)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

這裏簡單講解下作用

  • throttle(0.3, scheduler: MainScheduler.instance) 保證用戶沒有輸入0.3秒後再進行下一步
  • distinctUntilChanged() 假如0.3秒之前輸入是ab,0.3秒後還是ab,則不會進行下一步,只有改變了纔會進行下一步
  • flatMapLatest 保證只搜索最新的,如果之前的沒有完成,會被自動取消
  • doSearchAPI(query).retry(3) 保證,如果發生錯誤,自動重試3次

Schedulers

Schedulers 抽象化了線程,線程池,GCD中操作隊列,Runloop等概念。可以理解爲,Schedulers就是一個執行任務的線程。

有一點要注意:默認一個Observerable在其創建的線程上執行

與Schedulers相關的操作符有兩個

  • observeOn(scheduler) 在一個scheduler上執行任務,使用場景較多
  • subscribeOn(scheduler) 在一個scheduler進行監聽

比如

sequence1
  .observeOn(backgroundScheduler)
  .map { n in
      print("This is performed on the background scheduler")
  }
  .observeOn(MainScheduler.instance)
  .map { n in
      print("This is performed on the main scheduler")
  }.subscribeOn(backgroundScheduler)
  .subscribeNext{ n in
      print("This is performed on the background scheduler")
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

默認一個subscribeNext或者subscribe在其調用的線程上執行

Serial/Concurrent Schedulers 串行或並行

和GCD的隊列很相似,並行Schedulers的任務可以併發之行,串行Schedulers只能依次之行。不過RxSwift有內部機制,保證上文提到的信號處理的順序

RxSwift內置的Scheduler

通常,使用內置的Scheduler足矣。

  • CurrentThreadScheduler(串行) 當前線程Scheduler,默認使用的
  • MainScheduler(串行) 主線程
  • SerialDispatchQueueScheduler 封裝了GCD的串行隊列
  • ConcurrentDispatchQueueScheduler 封裝了GCD的並行隊列,這個在有任務要在後臺執行的時候很有用
  • OperationQueueScheduler 封裝了NSOperationQueue

例子四,在後臺Scheduler之行任務,然後在主線程上更新UI


Variable

Variable表示一個可監聽的數據結構。使用Variable,你可以監聽數據的變化,也可以把其他值綁定到它身上。

當Variable被釋放的時候,它會向監聽者發送onCompleted

例子五,Variable進行監聽

class VariableController: UIViewController {

    @IBOutlet weak var label: UILabel!
    var timer:NSTimer?
    var count = Variable(0)
    override func viewDidLoad() {
        super.viewDidLoad()
        timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector:#selector(VariableController.updateValue) , userInfo: nil, repeats: true)
        _ = count.asObservable().subscribeNext { (num) in
            self.label?.text = "VariableValue:\(num)"
        }
    }
    func updateValue(){
        count.value = count.value + 1
    }
    override func viewDidDisappear(animated: Bool) {
        super.viewDidDisappear(animated)
        timer?.invalidate()
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

數據綁定

數據綁定是開發的時候很常見的,比如根據文本的輸入動態調整textfield的背景色,動態調整按鈕的enable。亦或者根據textfield的輸入變化,動態的去反饋到model層。如果你聽過MVVM,那你肯定知道,MVVM的難點就是ViewModel與View的數據綁定問題。

不過,使用RxSwift,數據綁定變的十分容易,你甚至可以把數據綁定到tableview和collectionView上去。

例子六,bindTo 
很簡單,隨着Switch的開關,view進行顯示/隱藏

 
只需要一行代碼

_ = mySwitch.rx_value.bindTo(testView.rx_hidden)
  • 1
  • 1

例子七,根據輸入,進行View狀態綁定

我們想要實現這樣的狀態

  • 用戶名至少6位,小於6位,則背景色是灰色,合法則透明
  • 密碼至少位8位,小於8位,則背景色是灰色,合法則透明
  • 當用戶名和密碼都合法的時候,註冊按鈕enable,並且背景色變紅

信號的處理方式如下,

        let nameObserable = nameTextfield.rx_text.shareReplay(1).map({$0.characters.count >= 6}) 
        let pwdObserable = passwordTextfield.rx_text.shareReplay(1).map({$0.characters.count >= 8})

        _ = nameObserable.subscribeNext({ (valid) in
            self.nameTextfield.backgroundColor = valid ? UIColor.clearColor():UIColor.lightGrayColor()
        }).addDisposableTo(disposeBag)

        _ = pwdObserable.subscribeNext({ (valid) in
            self.passwordTextfield.backgroundColor = valid ? UIColor.clearColor(): UIColor.lightGrayColor()
        }).addDisposableTo(disposeBag)//addDisposableTo(disposeBag)是爲了自動釋放

        _ = Observable.combineLatest(nameObserable, pwdObserable) {$0 && $1}.subscribeNext({valid in
                if valid{
                    self.registerButton.enabled = true
                    self.registerButton.backgroundColor = UIColor.redColor()
                }else{
                    self.registerButton.enabled = false
                    self.registerButton.backgroundColor = UIColor.darkGrayColor()
                }
            }).addDisposableTo(disposeBag)
        _ = registerButton.rx_tap.shareReplay(1).subscribeNext {
            print("Button tapped")
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

共享監聽Sharing subscription-shareReplay

這個是很常用的,比如一個Obserable用做網絡請求,通常,當你這樣調用的時候,會創建兩個序列,也就是會進行兩次網絡請求,這是不需要的

let network = networkWithText(text)
let subscription1 = network
    .subscribeNext { n in
       //創建第一個序列
    }
let subscription2 = network
    .subscribeNext { n in
       //創建第二個序列
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

爲了共享一個序列,你只需要這這樣調用

let network = networkWithText(text).shareReplay(1)
  • 1
  • 1

就只會進行一次網絡請求,兩個subscription共享結果,也就是shareReplay的意思

自定義可綁定屬性

上文,textfield和button的狀態綁定是手動的,這無疑是不方便的。RxSwift爲我們提供了一種方式,來自定義可綁定屬性

創建兩個exetnsion

extension UITextField{
    var ex_validState:AnyObserver<Bool>{
        return UIBindingObserver(UIElement: self) { textfield, valid in
                textfield.backgroundColor = valid ? UIColor.clearColor():UIColor.lightGrayColor()
            }.asObserver()
    }
}
extension UIButton{
    var ex_validState:AnyObserver<Bool>{
        return UIBindingObserver(UIElement: self) { button, valid in
                button.enabled = valid
            button.backgroundColor = valid ? UIColor.redColor() : UIColor.darkGrayColor()
            }.asObserver()
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

然後,上文的代碼,就可以簡化成三行了,So easy

        _ = nameObserable.bindTo(nameTextfield.ex_validState).addDisposableTo(disposeBag)
        _ = pwdObserable.bindTo(passwordTextfield.ex_validState).addDisposableTo(disposeBag)

        _ = Observable.combineLatest(nameObserable, pwdObserable) {$0 && $1}.bindTo(registerButton.ex_validState).addDisposableTo(disposeBag)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

Driver

Driver是RxSwift精心製作的,專門提供給UI層的一個接口。

利用Driver你可以

  • 利用CoreData的模型來驅動UI
  • 利用UI的狀態來綁定其他UI的狀態

Driver能夠保證,在主線程上監聽,因爲UIKit不是需要在主線程上操作


Tips: 
RxSwift中做數據綁定有三種

  1. 利用BindTo方法
  2. 利用Driver(強烈建議使用這個,)
  3. 利用KVO來手動綁定(很少用到)

回到Driver上來,上文提到了,對於搜索,我們可以這麼做,

let results = query.rx_text
    .throttle(0.3, scheduler: MainScheduler.instance) //延遲0.3.flatMapLatest { query in //永遠只執行最新的
        searchWithText(query)
    }

results
    .map { "\($0.count)" } 
    .bindTo(resultCount.rx_text)//綁定label
    .addDisposableTo(disposeBag)

results
    .bindTo(resultsTableView.rx_itemsWithCellIdentifier("Cell")) { (_, result, cell) in //綁定tableview
        cell.textLabel?.text = "\(result)"
    }
    .addDisposableTo(disposeBag)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

那麼,這有什麼缺陷呢? 

  1. 假如searchWithText失敗了,那麼整個序列就斷掉了,後面的綁定不會有任何作用
  2. 假如searchWithText是在後臺線程執行的,那麼後續綁定是在後臺線程上進行的,會崩潰
  3. 綁定了兩次,意味着會執行兩次

於是,我們需要進行額外的操作,來避免上述缺陷。

let results = query.rx_text
    .throttle(0.3, scheduler: MainScheduler.instance) 
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
            .observeOn(MainScheduler.instance)  // 保證在主線程(解決缺陷1.catchErrorJustReturn([])           // 發生錯誤,返回空數組(解決缺陷2)
                }
    .shareReplay(1)                             // 共享監聽,保證只執行一次(解決缺陷3)                                               
results
    .map { "\($0.count)" }
    .bindTo(resultCount.rx_text)
    .addDisposableTo(disposeBag)

results
    .bindTo(resultTableView.rx_itemsWithCellIdentifier("Cell")) { (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .addDisposableTo(disposeBag)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

利用Driver我們可以將上述過程簡化

let results = query.rx_text.asDriver()        // 轉換成Driver序列
    .throttle(0.3, scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
            .asDriver(onErrorJustReturn: [])  // 告訴Driver發生錯誤怎麼辦
                }

results
    .map { "\($0.count)" }
    .drive(resultCount.rx_text)               // 用Driver綁定,不需要切換到主線程
    .addDisposableTo(disposeBag)                                                            
results
    .drive(resultTableView.rx_itemsWithCellIdentifier("Cell")) { (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .addDisposableTo(disposeBag)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

任何滿足以下三個條件的Observer序列都可以轉換爲Driver

  1. 不會因爲錯誤就序列斷掉(比如,有錯誤,但是沒有調用onError來發送錯誤)
  2. 在主線程傻姑娘監聽
  3. 共享 side effects

對於,使用者只需要調用asDriver(onErrorJustReturn: [])就能保證上述三點都實現了


KVO

通常的KVO,你需要在這個函數裏來處理

-(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary *)change
                      context:(void *)context
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

而使用RxSwift,KVO變成了這樣

view.rx_observe(CGRect.self, "frame")
    .subscribeNext { frame in
        print("Got new frame \(frame)")
    }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

或者這樣

someSuspiciousViewController
    .rx_observeWeakly(Bool.self, "behavingOk")
    .subscribeNext { behavingOk in
        print("Cats can purr? \(behavingOk)")
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

二者的區別是

在rx_observe可以使用地方都可以使用rx_observeWeakly。rx_observeWeakly的執行效率要低一點,因爲要處理對象的dealloc關係。除此之外,rx_observeWeakly還可以用在weak屬性上。

在使用view.rx_observe的時候,有幾點要注意

由於KVO是建立在NSObject子類的基礎上的,你可以通過如下方法,來讓Structs支持KVO


Notification

使用RxSwift,Notification變的十分簡潔

NSNotificationCenter.defaultCenter()
    .rx_notification(UITextViewTextDidBeginEditingNotification, object: myTextView)
    .map { /*do something with data*/ }
    ....
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

DEBUG

調試編譯問題

Swift是一個強類型語言,能夠在絕大部分場景自動推斷出變量的類型。比如

let  a = 10 //Int
  • 1
  • 1

但是,有些時候RxSwift的序列處理會報編譯錯誤 
比如

images = word
    .filter { $0.containsString("important") }
    .flatMap { word in
        return self.api.loadFlickrFeed("karate")
            .catchError { error in
                return just(JSON(1))
            }
      }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如果,Swift沒辦法推斷出類型,那麼最直接的方式,就是顯式的告訴Swift類型是什麼

images = word
    .filter { (s: String) -> Bool in s.containsString("important") } //輸入是 string ,返回Bool
    .flatMap { (word: String) -> Observable<JSON> in
        return self.api.loadFlickrFeed("karate")
            .catchError { (error: NSError) -> Observable<JSON> in
                return just(JSON(1))
            }
      }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

調試

使用Debug操作符,會log所有的數據流

let subscription = myInterval(0.1)
    .debug("my probe")
    .map { e in
        return "This is simply \(e)"
    }
    .subscribeNext { n in
        print(n)
    }

NSThread.sleepForTimeInterval(0.5)


subscription.dispose(
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

你可以用自定義操作符的方式,來Log

extension ObservableType {
    public func myDebug(identifier: String) -> Observable<Self.E> {
        return Observable.create { observer in
            print("subscribed \(identifier)")
            let subscription = self.subscribe { e in
                print("event \(identifier)  \(e)")
                switch e {
                case .Next(let value):
                    observer.on(.Next(value))

                case .Error(let error):
                    observer.on(.Error(error))

                case .Completed:
                    observer.on(.Completed)
                }
            }
            return AnonymousDisposable {
                   print("disposing \(identifier)")
                   subscription.dispose()
            }
        }
    }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

調試內存泄漏問題

RxSwift通過RxSwift.resourceCount記錄資源分配情況,所以通常的調試方式如下

  /* 在AppDelegate方法中添加Log
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
    */
    _ = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
        .subscribeNext { _ in
        print("Resource count \(RxSwift.resourceCount)")
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

然後

  1. 進入相關界面,進行正常操作
  2. 退出界面
  3. 觀察RxSwift.resourceCount
  4. 在進入同一個界面,退出
  5. 觀察RxSwift.resourceCount

使用心得

  1. 時刻牢記,使用RxSwift,儘量把所有的任務(可以理解爲方法)抽象成Obserable(序列)和Obserable創建者,監聽者
  2. 能用數據綁定的(bindTo和Driver)的就不要手動綁定
  3. 一定要熟練RxSwift提供的操作符,要會自定義操作符

RxSwift的優點

  • Composable 可組合,在設計模式中有一種模式叫做組合模式,你可以方便的用不同的組合實現不同的類
  • Reusable 代碼可重用,原因很簡單,對應RxSwift,就是一堆Obserable
  • Declarative 響應式的,因爲狀態不可變,只有數據變化
  • Understandable and concise 簡潔,容易理解。
  • Stable 穩定,因爲RxSwift寫出的代碼,單元測試時分方便
  • Less stateful “無”狀態性,因爲對於響應式編程,你的應用程序就是一堆數據流
  • Without leaks 沒有泄漏,因爲資源管理非常簡單

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章