開發也能夠改變下,RxSwift-讓你的開發變得簡潔高效。

RxSwift究竟是什麼?

RxSwift是一種函數式響應式編程。那什麼是函數式編程呢,函數式編程最重要的概念就是「無狀態(immutable)」,看到這有些小夥伴可能會很開心,無狀態(知名LOL職業選手)嘛,我是他的粉絲!言歸正傳,到底什麼是「無狀態(immutable)」呢?我看了不少文章,可是都被他們專業的描述整的一頭霧水,我來講說個人見解:有豐富debug經驗的小夥伴們都知道一個事實,程序中80%的BUG均可以由追蹤變量的值來發現,爲何呢?反過來想,引發BUG的最大元兇正是值的改變。說道這裏你們確定都有了一個大膽的想法,假若有一種方式可以消除狀態的改變(這裏爲何不說「值」,由於Rx不只僅是值的「無狀態html

(immutable)」,也會有事件的「無狀態(immutable)」,因此準確的描述應該是「狀態」),那麼豈不是就沒什麼BUG了?函數式編程正是爲此而生。react

那函數式編程到底是怎麼作到「無狀態(immutable)」這一點的呢?答案就是「序列(sequence)」,既然我不能改變狀態,那我須要發生改變的時候,我就從新生成一個狀態,發生幾回改變,我就生成幾個狀態,而「序列(sequence)」就是盛放這些狀態的容器(你們能夠看作是一個數組),那麼狀態的改變是否是就不復存在?答案是確定的,我只須要分別對「序列(sequence)」中的狀態作出命令,他們之間就不會互相影響,那麼出bug的概率天然大大下降。面試

那麼既然成爲了一個「序列」,怎麼生成「序列」,怎麼組合多個「序列」,怎麼處理每一個「序列」中的每一個元素?RxSwift中有很是多的操做符來解決這些問題,這也是RxSwift難以學習的緣由之一。可是咱們只要理解,這些操做符的的最終目標是讓「序列」變成咱們想要的樣子。記憶起來就會事半功倍。 說完函數式,再來講說響應式編程,先看段代碼:編程

var a: Int = 0
 var b: Int = 0
 var c = a + b
 a = 1
 print(c)

複製代碼

儘管a在c打印以前賦值爲1,可是在給c賦值的時候 a + b 仍是等於 0 ,swift

咱們要想讓c獲得最新的結果,就須要在c賦值以前將a的值改成1,數組

那麼若是a和b的值在以後的代碼中會一直變更,promise

就須要不斷的給c寫一行從新賦值的代碼:c = a+ b,這真是太麻煩了,markdown

響應式就是爲了解決這個麻煩誕生的,當a或者b發生變化的時候,網絡

c的值會響應a或者b的值發生變化,因此就被稱之爲響應式了,那麼響應式如何實現呢,閉包

最簡單容易理解的方式就是經過a或者b的屬性監聽器,當監聽到a或者b發生變化時,

就從新給c賦值。實際上,不管代碼怎麼寫,無非是隱式了c = a + b這一行代碼而已,

即便咱們直接去操做指針,也須要有一行代碼將兩個指針指向的值相加,

因此響應式並非難以理解的技術,可是響應式編程可以極大的改善代碼的可讀性,

更直觀,更容易維護。

RxSwift,就是將函數式,響應式,以及鏈式編程的結合體,即便在項目中因爲各類緣由不能去使用,讀懂它,也能讓你在寫業務代碼的時候更加遊刃有餘。

在這裏再多說幾句:有不少博客把「序列(sequence)」比做「流、信號、管道」等,這確實有助於你們理解RxSwift,可是RxSwift的項目維護者顯然不這麼認爲,下面給你們看一段InfoQ對項目維護者Krunoslav Zaher的採訪:

使用不同的、不太準確的方式思考可觀察的序列會帶來不少問題(流、信號、導管等)。那些東西有時候能夠用來幫助新手探索Rx,可是若是使用的時間過長,它們只會在接下來的開發中帶來不少困惑。人們有時候使用它們是由於Rx能夠用在模擬系統的多狀態部分,可是那只是故事的一部分,而我建議你們不要那樣思考。

使用那些項(流、信號、導管等)思考的問題在於它們帶有一個隱含假定:

定義Observable時帶有共享的值,而且能夠從外部設定的當前值也是同樣的。而Observable會被莫名其妙地直接取消,這看起來像future或promise。

而事實上:

Observable只是一個如何計算序列的定義。

沒有計算會在序列定義後(let result = source.map(transformObservableSequence))自動地執行。這和Swift中的SequenceSequence是同樣的。當綁定一個觀察者時(這與調用SequenceType中的generate方法相同)纔會執行計算,

而且能夠經過處理Disposable對象的結果來取消特定的計算。

Observable固然能夠表明多狀態的計算,它們共享潛在的綁定並source可觀察的序列(使用shareReplay(1)操做等),可是這不是默認的行爲。

我認爲部分的問題在於也許人們一直在使用future、promise或其餘更簡單的、使用方式類似的reactive庫,因此他們天然地認爲Rx在其餘的狀況下也是一樣的表現,而這對於新手來講顯然是使人困惑的。

奇怪的是那些單方面的屬性對剛開始使用Rx的人們形成了很大的問題,有時候門檻會變得過高以致於人們變的沒有動力,可是從另外一角度來看,這正是我我的認爲Rx美的緣由。

它美的地方在於你能夠只經過一句話就能教會一我的使用Rx:Observable表明了元素類型爲T的可觀察序列(與Swift中的SequenceSequence是同樣的),其中每一個元素均可以調用subscribe(和SequenceSequence中的generate方法相同)方法來註冊本身的接受序列元素的observer(返回信號)。

RxSwift官方demo解析 下面是官網demo的一個小模塊,實現了Github的註冊功能,咱們先看一下目錄結構(沒有標綠的文件是RxSwift專門爲UI設計的更爲精簡的實現方式,這裏就很少作探討):

image

顯然這是一個標準的MVVM架構,在我看來RxSwift是MVVM的完美CP,RxSwift很是適合用來解決MVVM中,ViewModel的State和View綁定的問題。再扯一些題外話,demo中使用到了面向協議編程的思想,也是很值得你們參考的,可以大幅度提升代碼的複用率和解耦合。推薦你們在慕課網上搜索2016swift開發者大會上,李潔信的演講,在這裏我就不附上地址了。

接着看療效:

image

在正常狀況下,完成2個密碼的比對功能咱們須要在textField的代理方法中監聽text屬性的變化,而後使用相似於這種結構的判斷:

if  validPassWord(textfield.text) {
      tipLabel1.text = ...
      tipLabel.textColor = ...
} else {
      code
}

if textfield1.text == textfield2.text {
      tipLabel2.text = ...
      tipLabel2.textColor = ...
}else {
      code
}
複製代碼

事實上,實際狀況遠比個人僞代碼複雜的多得多,你們應該都深有體會。那麼RxSwift又是怎麼完成這些功能的呢?咱們先看一下viewController中的代碼(爲了方便你們理解我會添加註釋):

override func viewDidLoad() {
        super.viewDidLoad()
        //  這裏是viewModel的構造函數,構造函數會接收三個TextField中的text生成的「序列」
        //  只要textField.text發生改變,「序列」中就會有一個新的元素Element生成
        let viewModel = GithubSignupViewModel1(
            input: (
                // .rx.text.orEmpty.asObservable()是RxSwift提供的UIKit的擴展,能夠快速生成一個可觀察序列,如下同理   
                username: usernameOutlet.rx.text.orEmpty.asObservable(),
                password: passwordOutlet.rx.text.orEmpty.asObservable(),
                repeatedPassword: repeatedPasswordOutlet.rx.text.orEmpty.asObservable(),
                loginTaps: signupOutlet.rx.tap.asObservable()
            ),
            dependency: (
                //  網絡接口
                API: GitHubDefaultAPI.sharedAPI,
                //  驗證輸入內容是否合法的工具類
                validationService: GitHubDefaultValidationService.sharedValidationService,
                //  彈窗,點擊登陸按鈕後提示信息
                wireframe: DefaultWireframe.shared
            )
        )

        //  viewModel構造函數中會處理出入的三個「序列」,而後生成如下五個可觀察「序列」
        //  let validatedUsername: Observable<ValidationResult>          //  用戶名是否可用
        //  let validatedPassword: Observable<ValidationResult>          //  密碼是否可用
        //  let validatedPasswordRepeated: Observable<ValidationResult>  //  重複密碼是否可用
        //  let signupEnabled: Observable<Bool>                          //  註冊按鈕是否能夠點擊
        //  let signedIn: Observable<Bool>                               //  是否玩成了註冊(控制彈窗)
        //  let signingIn: Observable<Bool>                              //  是否在註冊進程中(控制菊花的旋轉)
       //   ValidationResult是一個枚舉
       enum ValidationResult {
            case ok(message: String)
            case empty
            case validating
            case failed(message: String)
       }
        // 有了以上這些序列,就能夠在控制器中完成數據和控件的綁定
        viewModel.signupEnabled
            //  訂閱一個可觀察序列的事件處理閉包,只要有新的元素添加到序列中,該方法就會自動發出信號並執行閉包中的代碼,並返回一個遵照Disposable的類型,Disposable就至關於垃圾回收,會自動銷燬序列。
            .subscribe(onNext: { [weak self] valid  in
                self?.signupOutlet.isEnabled = valid
                self?.signupOutlet.alpha = valid ? 1.0 : 0.5
            })
            .disposed(by: disposeBag)

        viewModel.validatedUsername
            /**
            bind其實跟subscribe是等價的,只是他能更好的反應可觀察序列和觀察者之間的關係
            站在一個代碼閱讀者的角度,用subscribe方法訂閱,你可能須要在事件處理序列中去尋找誰是觀察者,可是使用bind,你
            能夠很直觀的看到觀察者是誰,其實就是至關於對subscribe再次封裝一層,讓觀察者在本身的代碼區域完成事
            件,就不用再寫在控制器中,代碼閱讀起來真叫一個流暢,有一種一目瞭然的感受。也讓你不知不覺中就下降了
            代碼的耦合度。
            */
            .bind(to: usernameValidationOutlet.rx.validationResult)
            .disposed(by: disposeBag)

        viewModel.validatedPassword
            .bind(to: passwordValidationOutlet.rx.validationResult)
            .disposed(by: disposeBag)

        viewModel.validatedPasswordRepeated
            .bind(to: repeatedPasswordValidationOutlet.rx.validationResult)
            .disposed(by: disposeBag)

        viewModel.signingIn
            .bind(to: signingUpOulet.rx.isAnimating)
            .disposed(by: disposeBag)

        viewModel.signedIn
            .subscribe(onNext: { signedIn in
                print("User signed in \(signedIn)")
            })
            .disposed(by: disposeBag)
        //}

        let tapBackground = UITapGestureRecognizer()
        tapBackground.rx.event
            .subscribe(onNext: { [weak self] _ in
                self?.view.endEditing(true)
            })
            .disposed(by: disposeBag)
        view.addGestureRecognizer(tapBackground)
    }

複製代碼

總結

原本很想詳細的解構一下官方demo中的代碼解構,

真的是十分優秀簡潔的代碼,而後再說一說每一個操做符的含義,想讓尚未開始接觸到RxSwift的小夥伴稍稍感覺一下RxSwift的魅力所在。

在嚴峻的iOS市場中競爭, 沒有掌握和了解一些具備優點的技術性知識,你怎麼與別人進行PK,又怎麼征服你的面試官。作過三五年的也未必必定會涉足到裏面的技術性知識。在找工做中,技術足了,找不到工做也許你缺乏的是一份總結和一份面試題去練習。

[最新面試大廠常問面試答案一份和iOS技術資源 ]。但願真心可以幫助到你們提高技術!

若是你由於本文稍稍對RxSwift提起興趣,並打算學學看,那麼下面這些博文千萬不要錯過: RxSwift函數響應式編程 RxSwift給Swift帶來了原生Reactive編程的功能 是時候學習RxSwift了 [翻譯]RxSwift入門

但願讀者能指出本文中出現的錯誤,不勝感激。

關注下面的標籤,發現更多類似文章

相關文章
相關標籤/搜索