【RxSwift 實踐系列 1/3】爲何使用RxSwift

  • 從mvvm架構開始講起
  • 舉個栗子:RxSwift 能作什麼

從mvvm架構開始講起

MVC是目前主流的客戶端編程框架。在iOS開發中,系統爲咱們實現好了公共的視圖類:UIView 和控制器類:UIViewController。編程

開發過程當中,你必定在Controller中寫過爲View格式化數據的代碼,爲何咱們就這麼天然的把格式化數據的代碼放到了Controller,一個很直接的答案,就是M和V都不適合。 格式化數據的代碼確定不適合放在Model裏,而View只應該負責爲用戶顯示內容,它徹底不該該關心本身具體顯示的是什麼? 因而,就只剩下Controller了,索性就塞給它吧,因而隨着咱們的UI愈加複雜,Controller就越臃腫,也不容易作測試,更別說複用了。swift

MVC這種分層方式雖然清楚,可是若是使用不當,大量代碼都集中在Controller之中,viewControllers有很大機率充斥着各類既不適合放在model也不適合放在view裏的代碼。項目過大後,Controller的優化歷來沒有中止過,總結了一些方案:bash

1. 將 UITableView 的 Data Source 分離到另一個類中。

2. 將數據獲取和轉換的邏輯分別到另一個類中。

3. 將拼裝控件的邏輯,分離到另一個類中。

總結來就是Controller裏只放不能複用的代碼
複製代碼

相對於 MVC 的歷史來講,MVVM 是一個至關新的架構,MVVM 最先於2005年被微軟的WPF和 Silverlight 的架構師 John Gossman 提出,而且應用在微軟的軟件開發中。當時 MVC 已經被提出了 20 多年了,可見二者出現的年代差異有多大。架構

MVC:

Model <-> Controller <-> View

MVVM:

Model <-> ViewModel <-> Controller <-> View
複製代碼

能夠看到,這個View Model就是MVVM新引入的東西。一方面,它替代Model爲Controller提供了全部的數據接口;另外一方面,他也替代了Controller向Model寫回數據。這樣Controller就能夠只專一於從數據到視圖的過渡。在後面的視頻中咱們就會看到,這樣作能夠有效的改善Controller的體積以及可測試性。app

一、View不該瞭解任何Controller的細節,它只是一個用於展現內容的白板,給它什麼,它就顯示什麼。不管是MVC,仍是MVVM,這都是必定要遵循的原則;框架

二、Controller不該該瞭解任何Model的細節。less

三、View Model擁有 Model 。在原來的MVC模式中,Model 是被 Controller 擁有的,可是在 MVVM 中,Model被 View Model 持有。mvvm

四、Model不該該瞭解擁有它的View Model。ide

MVVM 在使用當中,一般還會利用雙向綁定技術,使得 Model 變化時,ViewModel 會自動更新,而 ViewModel 變化時,View 也會自動變化。因此,MVVM模式有些時候又被稱做:model-view-binder 模式。在 iOS 中,可使用 KVO 或 Notification 技術達到這種效果,由於KVO的代碼複雜,衍生出了ReactiveCocoa,Rxswift 的工具.他們就是響應式編程。函數式編程

在講以前,咱們須要瞭解如下概念:函數式編程(Functional Programming)和響應式編程(React Programming)它們的結合能夠很方便地實現數據的綁定。

函數式編程(Functional Programming),函數也變成一等公民了,能夠擁有和對象一樣的功能,例如當成參數傳遞,看成返回值等。

響應式編程(React Programming),原來咱們基於事件(Event)的處理方式都弱了,如今是基於輸入(在 ReactiveCocoa 裏叫 Signal)的處理方式。輸入還能夠經過函數式編程進行各類 Combine 或 Filter,盡顯各類靈活的處理。

無狀態(Stateless),狀態是函數的魔鬼,無狀態使得函數能更好地測試。

不可修改(Immutable),數據都是不可修改的,使得軟件邏輯簡單,也能夠更好地測試。

RxSwift 核心概念就是一個觀察者( Observer )訂閱一個可觀察序列( Observable )。觀察者對 Observable 發射的數據或數據序列做出響應。現實世界也是如此:你等待老闆的安排對老闆發出指令作出響應、你坐在家裏等着媽媽發出吃飯的指令,你去吃飯。

舉個例子:RxSwift能作什麼

學習 RxSwift 前,先看從幾個簡單的例子看看RxSwift能作什麼

Observable.combineLatest(firstName.rx.text, lastName.rx.text) { "\($0!) \($1!)" }
        .map { "Greetings, \($0)" }
        .bind(to: greetingLabel.rx.text)
        .disposed(by: rx.disposeBag)
複製代碼

這段是官方的例子,他作的事情是:

1. 將 firstName 和 lastName 的 text 值用空格合併起來做爲結果傳遞給下一步使用
2. 使用 map 的方法,將上一步獲得值前面加上一個 Greeting ,並將該值傳遞給後面使用
3. bindTo 就是綁定,將上一步的值綁定到 greetingLabel 的 text
4. disposed最後作一次資源回收
複製代碼

最終的效果:當用戶在 firstName 和 lastName 的 textfile 上輸入任何字符,greetingLabel上就會響應,顯示最新的輸入 Greeting+firstName+""+lastName

  • greetingLabel是一個觀察者( Observer )訂閱一個可觀察序列( Observable )firstName 和 lastName,greetingLabel對firstName 和 lastName接收到的用戶輸入作出相應

若是換作傳統的方式實現對UITextField的監聽須要怎樣實現呢:

1. 代理方式

//須要繼承UITextFieldDelegate
override func viewDidLoad() {
    super.viewDidLoad()
    firstName.delegate = self
    lastName.delegate = self
}
    
var firstNameString = ""
var lastNameString = ""
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if(textField == firstName){
        firstNameString = textField.text!
    }
    if(textField == lastName){
        lastNameString = textField.text!
    }
    greetingLabel.text = "Greetings, \(firstNameString) \(lastNameString)"
    return true
}
複製代碼

二、KVO方式

override func viewDidLoad() {
    super.viewDidLoad()
    firstName.addObserver(self, forKeyPath: "text", options: .new, context: nil)
    lastName.addObserver(self, forKeyPath: "text", options: .new , context: nil)
    view.addSubview(firstName)
    view.addSubview(lastName)
    view.addSubview(greetingLabel)
}
    
//非實時的變化
var firstNameString = ""
var lastNameString = ""
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if (object as! UITextField == firstName) {
        firstNameString = firstName.text!
    }
    if (object as! UITextField == lastName) {
        lastNameString = lastName.text!
    }
    greetingLabel.text = "Greetings, \(firstNameString) \(lastNameString)"
}
複製代碼

三、通知方式

override func viewDidLoad() {
    super.viewDidLoad()
    firstName.addTarget(self, action: #selector(fieldChange), for: .editingChanged)
    lastName.addTarget(self, action: #selector(fieldChange), for: .editingChanged)
    view.addSubview(firstName)
    view.addSubview(lastName)
    view.addSubview(greetingLabel)
}
    
var firstNameString = ""
var lastNameString = ""
@objc func fieldChange(textField: UITextField){
    if(textField == firstName){
        firstNameString = textField.text!
    }
    if(textField == lastName){
        lastNameString = textField.text!
    }
    greetingLabel.text = "Greetings, \(firstNameString) \(lastNameString)"
}
複製代碼

四、直接添加監視

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(fieldChange), name: .UITextFieldTextDidChange, object: firstName)
    NotificationCenter.default.addObserver(self, selector: #selector(fieldChange), name: .UITextFieldTextDidChange, object: lastName)
    view.addSubview(firstName)
    view.addSubview(lastName)
    view.addSubview(greetingLabel)
}
    
var firstNameString = ""
var lastNameString = ""
@objc func fieldChange(notify:NSNotification){
    let textfield = notify.object as! UITextField
    if (textfield == firstName){
        firstNameString = textfield.text!
    }
    if (textfield == lastName){
        lastNameString = textfield.text!
    }
    greetingLabel.text = "Greetings, \(firstNameString) \(lastNameString)"
}
複製代碼

相信你已經看出RxSwift的思想和他簡潔代碼的魅力了吧,別急,接下來咱們來學習怎麼從RxSwift官方文檔來學習它,以後咱們將實踐作一個app,一邊作一邊學習其中的知識點。

【RxSwift 實踐系列 2/3】thinking in Rx- Create和Drive

相關文章
相關標籤/搜索