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設計的更爲精簡的實現方式,這裏就很少作探討):
顯然這是一個標準的MVVM架構,在我看來RxSwift是MVVM的完美CP,RxSwift很是適合用來解決MVVM中,ViewModel的State和View綁定的問題。再扯一些題外話,demo中使用到了面向協議編程的思想,也是很值得你們參考的,可以大幅度提升代碼的複用率和解耦合。推薦你們在慕課網上搜索2016swift開發者大會上,李潔信的演講,在這裏我就不附上地址了。
在正常狀況下,完成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入門
但願讀者能指出本文中出現的錯誤,不勝感激。
關注下面的標籤,發現更多類似文章