前言
寫這篇文章是為了記錄下自己在對于RxSwift的學(xué)習(xí)過程中的概念理解绑榴,操作步驟以及心得體會,以便于在以后復(fù)習(xí)所用。如果文中有任何錯誤的地方稚虎,還請各位看官老爺們指正...(先捂上臉??)
函數(shù)響應(yīng)式編程
在學(xué)習(xí)RxSwift之前,先來了解一下函數(shù)響應(yīng)式編程的思想偎捎,我們可以把函數(shù)響應(yīng)式編程拆開來看蠢终,分為函數(shù)式編程和響應(yīng)式編程來理解:
1.函數(shù)式編程
函數(shù)式編程簡稱FP(Functional Programming),函數(shù)式編程就是一種抽象程度很高的編程范式茴她,它將計算機運算看做是數(shù)學(xué)中函數(shù)的計算寻拂,而純粹的函數(shù)式編程語言編寫的函數(shù)沒有變量,因此丈牢,任意一個函數(shù)兜喻,只要輸入是確定的,輸出就是確定的赡麦,這種純函數(shù)我們稱之為沒有副作用朴皆。而允許使用變量的程序設(shè)計語言,由于函數(shù)內(nèi)部的變量狀態(tài)不確定泛粹,同樣的輸入遂铡,可能得到不同的輸出,因此晶姊,這種函數(shù)是有副作用的扒接。
函數(shù)式編程的一個特點就是:允許把函數(shù)本身作為參數(shù)傳入另一個函數(shù),同時還允許返回一個函數(shù)们衙!
函數(shù)表達式: y = f(x) ---> x = f(x) ---> y = f(f(x))
下面寫一個栗子??來理解一下:
有這樣一個需求:對于數(shù)組[1,2,3,4,5,6,7]钾怔,首先獲取 > 3的數(shù)字,獲取到的數(shù)字之后 + 1蒙挑,再輸出所有數(shù)字中的偶數(shù)
let array = [1,2,3,4,5,6,7]
for num in array{
if num > 3{
let number = num + 1
if (number % 2 == 0) {
print(number)
}
}
}
這里我們利用行為式思路解決了這個需求宗侦,下面來換個思路來解決,使用 Array
的filter
方法;
let array = [1,2,3,4,5,6,7]
array.filter{ $0 > 3}
.filter{ ($0+1) % 2 == 0 }
.forEach { print($0) }
這里 array.filter
函數(shù)接受一個閉包類型的參數(shù)忆蚀,filter
方法會對 array
中的每一個元素都用傳入filter
的閉包調(diào)用一遍矾利,根據(jù)這個閉包的返回值決定是否將這個元素作為符合條件的元素加入我們的查找結(jié)果中。
簡單來說我們只需要在傳入的閉包中聲明好查找的規(guī)則馋袜,這樣我們就完成整個查找操作的處理了男旗。我們這里并沒有告訴程序應(yīng)該怎么去查找滿足條件的元素的方法,而只是聲明了一個規(guī)則欣鳖。這樣做最大的好處就是能夠減少我們的代碼量察皇,讓我們的代碼看起來非常的簡潔,而且易理解泽台。 這種方式就是函數(shù)式編程的一個例子什荣。
2.響應(yīng)式編程
響應(yīng)式編程簡稱RP(Reactive Programming),響應(yīng)式編程是一種面向數(shù)據(jù)流和變化傳播的異步編程范式呀忧。這意味著可以在編程語言中很方便地表達靜態(tài)或動態(tài)的數(shù)據(jù)流,而相關(guān)的計算模型會自動將變化的值通過數(shù)據(jù)流進行傳播溃睹。
簡單的來說就是基于事件流的編程方式而账。事件流就是將要發(fā)生事件按照時間順序排序發(fā)生形成的。而當這個事件流產(chǎn)生了返回的數(shù)據(jù)因篇,可以立刻得到通知并調(diào)用回調(diào)函數(shù)去處理數(shù)據(jù)泞辐。(在現(xiàn)實生活中就是:我在家拿起手機預(yù)訂了一份外賣,然后商家會收到訂單竞滓,開始制作外賣咐吼,制作完成后會通知騎手取餐,騎手接單取餐商佑,然后送餐锯茄,到達目的地,外賣送達茶没,訂單完成肌幽,這樣一系列的事件,先稱它為事件流抓半,如果在這些事件流進行的過程中喂急,比如說商家沒有出餐,或者騎手沒有取餐等等笛求,這些都會導(dǎo)致這個事件流無法完成廊移,而我們可以根據(jù)這個事件流中的任何一個事件的結(jié)果來進行響應(yīng))
從這個小例子可以看到組成響應(yīng)式編程的三種動作(數(shù)據(jù)):事件(數(shù)據(jù)),錯誤探入,結(jié)束狡孔。通過得到這三個響應(yīng)式動作,我們就可以在程序中作出不同的響應(yīng)蜂嗽。
這里個人覺得有點很重要苗膝,就是要對事件進行“預(yù)訂/監(jiān)聽”,然后才能收到這個事件的反饋徒爹。而對于我而言荚醒,我就是監(jiān)聽事件的人芋类,也就是“觀察者/訂閱方”隆嗅,監(jiān)聽+觀察者是不是就是我們比較熟悉的觀察者模式,那么響應(yīng)式編程就是觀察者模式+事件流的控制侯繁。
3.函數(shù)響應(yīng)式編程
函數(shù)響應(yīng)式編程 FRP(Functional Reactive Programming)是函數(shù)式編程與響應(yīng)式編程相結(jié)合起來的胖喳,響應(yīng)式編程思想為體, 函數(shù)式編程思想為用。(比較經(jīng)典的框架就是RAC和RxSwift)贮竟,常常有人說丽焊,F(xiàn)RP能讓你的代碼像數(shù)學(xué)一樣簡潔较剃,業(yè)務(wù)像流水一樣清晰流暢。
RxSwift初識
1.RxSwift簡介
首先技健,ReactiveX
(簡寫: Rx
) 是一個可以幫助我們簡化異步編程的框架,簡單來說就是基于異步 Event
序列的響應(yīng)式編程,并提供更優(yōu)雅的數(shù)據(jù)綁定,可以時刻響應(yīng)新的數(shù)據(jù)同時順序地處理它們写穴。RxSwift
(ReactiveX for Swift)
,就是ReactiveX
的Swift
版本
ReactiveX
家族非常強大,就如同‘毒液家族’一樣的強大雌贱,除了我后面會學(xué)習(xí)的 RxSwift
之外啊送,還有 RAC(ReactiveCocoa), RxJava, RxJS, RxKotlin, Rx.NET
...等等.
2.RxSwift導(dǎo)入配置
(1) 手動導(dǎo)入
- 從 RxSwift GitHub 上下載最新的代碼,
- 將下載下來的源碼包中
Rx.xcodeproj
拖拽至工程中, -
Project -> Targets -> General -> Embedded Binaries
配置項,RxSwift.framework
欣孤、RxCocoa.framework
添加進來即可 - 在需要使用
RxSwift
的地方import
進來
(2) CocoaPods導(dǎo)入
# Podfile
use_frameworks!
target 'YOUR_TARGET_NAME' do
pod 'RxSwift', '~> 5.0'
pod 'RxCocoa', '~> 5.0'
end
替換 YOUR_TARGET_NAME
然后在 Podfile
目錄下, 終端輸入:
$ pod install
??????這里說明一下為什么會導(dǎo)入
RxSwift
和RxCocoa
兩個庫馋没,它們的作用分別是:
RxSwift
:它只是基于Swift
語言的Rx
標準實現(xiàn)接口庫,所以RxSwift
里不包含任何Cocoa
或者UI
方面的類降传。RxCocoa
:是基于RxSwift
針對于iOS
開發(fā)的一個庫篷朵,它通過Extension
的方法給原生的比如UI
控件添加了Rx
的特性,使得我們更容易訂閱和響應(yīng)這些控件的事件婆排。
3.RxSwift特性
- 復(fù)合 -
Rx
就是和復(fù)合的代名詞 - 復(fù)用 - 復(fù)用性比較強 - 代碼量降低
- 清晰 - 因為聲明都是不可變更声旺,代碼函數(shù)式編程可讀性強
- 易用 - 理解容易,還抽象的了異步編程段只,統(tǒng)一代碼風(fēng)格
- 穩(wěn)定 - 因為
Rx
是完全通過單元測試的 - 裝X - 代碼的風(fēng)格很明顯比原?好
究竟是不是這樣的呢艾少,下面通過一些常用的方式來驗證一下:
(1)KVO
一般寫法:
func setupKVO() {
self.person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
person.name = "\(person.name) +"
// print(person.name)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("響應(yīng)")
print(change as Any)
}
deinit {
self.removeObserver(self.person, forKeyPath: "name", context: nil)
}
RxSwift寫法:
func setupKVO() {
self.person.rx.observeWeakly(String.self, "name")
.subscribe(onNext: { (value) in
print(value as Any)
})
.disposed(by: disposeBag)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
person.name = "\(person.name) +"
// print(person.name)
}
是不是感覺
RxSwift
版本的KVO
寫的代碼更少了,而且也不用去實現(xiàn)觀察者的代理方法翼悴,不用關(guān)心是否遺漏removeObserver
方法缚够。
(2)Target Action
一般寫法:
func setupButton() {
button.addTarget(self, action: #selector(didClickButton), for: .touchUpInside)
}
@objc func didClickButton(){
print("點我干什么")
}
RxSwift寫法:
func setupButton() {
self.button.rx.tap
.subscribe(onNext: { () in
print("點擊事件")
})
.disposed(by: disposeBag)
}
不需要實現(xiàn)
Target Action
,代碼更加簡單了
(3)代理
一般寫法:
class ViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
}
}
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("contentOffset: \(scrollView.contentOffset)")
}
}
RxSwift寫法:
class ViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
scrollView.rx.contentOffset
.subscribe(onNext: { contentOffset in
print("contentOffset: \(contentOffset)")
})
.disposed(by: disposeBag)
}
}
不需要實現(xiàn)代理方法啦,可以直接獲取到
scrollview
的偏移
(4)通知
一般寫法:
var testObserver: NSObjectProtocol!
override func viewDidLoad() {
super.viewDidLoad()
testObserver = NotificationCenter.default.addObserver(
forName: .UIApplicationWillEnterForeground,
object: nil, queue: nil) { (notification) in
print("Application Will Enter Foreground")
}
}
deinit {
NotificationCenter.default.removeObserver(testObserver)
}
RxSwift寫法:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.rx
.notification(.UIApplicationWillEnterForeground)
.subscribe(onNext: { (notification) in
print("Application Will Enter Foreground")
})
.disposed(by: disposeBag)
}
不需要去關(guān)注是否已經(jīng)移除了通知鹦赎,不會因為沒有移除通知而出現(xiàn)崩潰
(5)單擊手勢
一般寫法:
func setupGestureRecognizer(){
let tap = UITapGestureRecognizer()
tap.addTarget(self, action: #selector(singleTap(_:)))
self.label.addGestureRecognizer(tap)
self.label.isUserInteractionEnabled = true
}
@objc func singleTap(_ tapGesture: UITapGestureRecognizer) {
print("點我干嘛")
}
RxSwift寫法:
func setupGestureRecognizer(){
let tap = UITapGestureRecognizer()
self.label.addGestureRecognizer(tap)
self.label.isUserInteractionEnabled = true
tap.rx.event.subscribe(onNext: { (tap) in
print(tap.view)
})
.disposed(by: disposeBag)
}
少寫了手勢觸發(fā)實現(xiàn)的方法谍椅,代碼更簡介了
(6)Timer定時器
一般寫法:
var testtimer = Timer()
func setupTimer() {
testtimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(UpdateTimer), userInfo: nil, repeats: true)
testtimer.fire();
RunLoop.current.add(testtimer, forMode: .common)
}
@objc func UpdateTimer() {
print("timer start")
}
RxSwift寫法:
var timer: Observable<Int>!
func setupTimer() {
timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
timer.subscribe(onNext: { (num) in
print(num)
})
.disposed(by: disposeBag)
}
這里
timer
更好地是不用考慮頁面上的Scrollview
對timer
的影響
看完這些小例子??之后,覺得RxSwift
比一般的Swift
寫法要簡單好多古话,簡直就是“騷一般的操作”雏吭。
4.RxSwift核心邏輯初識
在前面的概念介紹中大概已經(jīng)知道了ReactiveX
是基于異步 Event
序列的響應(yīng)式編程, 并提供更優(yōu)雅的數(shù)據(jù)綁定,可以時刻響應(yīng)新的數(shù)據(jù)同時順序地處理它們,既然要響應(yīng)數(shù)據(jù),就還需要一個觀察者陪踩。
RxSwift 核心概念就可以理解為一個觀察者(Observer
)訂閱一個可被觀察序列(Observable
)杖们。觀察者對可被觀察序列發(fā)射的事件(Event
)或事件序列作出響應(yīng)。
先來理解一下這三個類的概念:
-
Observable<T>
這個類就是Rx
框架的基礎(chǔ)肩狂,我們可以稱它為可觀察序列摘完。它的作用就是可以異步地產(chǎn)生一系列的Event
(事件),即一個Observable<T>
對象會隨著時間推移不定期地發(fā)出event(element : T)
這樣一個東西傻谁。 - 而且這些
Event
還可以攜帶數(shù)據(jù)孝治,它的泛型<T>
就是用來指定這個Event
攜帶的數(shù)據(jù)的類型。 - 有了可觀察序列,我們還需要有一個
Observer
(訂閱者)來訂閱它谈飒,這樣這個訂閱者才能收到Observable<T>
不時發(fā)出的Event
岂座。
既然Observable
是一個可被觀察的序列,可以異步地產(chǎn)生一系列的 Event
杭措,那么查看RxSwift/Event.swift
源碼可以發(fā)現(xiàn)
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
}
Event
被定義成一個枚舉值费什,這也就是說一個Observable
可觀察序列可以發(fā)出三種不同類型的Event
事件:
-
next:
next
事件就是那個可以攜帶數(shù)據(jù)<T>
的事件,可以說它就是一個普通事件
-
error:
error
事件表示一個錯誤手素,它可以攜帶具體的錯誤內(nèi)容吕喘,一旦Observable
發(fā)出了error
event,則這個
Observable就等于終止了刑桑,以后它再也不會發(fā)出
event` 事件了氯质。
-
completed:
completed
事件表示Observable
發(fā)出的事件正常地結(jié)束了,跟error
一樣祠斧,一旦Observable
發(fā)出了completed event
闻察,則這個Observable
就等于終止了,以后它再也不會發(fā)出event
事件了
序列監(jiān)聽有三個步驟:1.創(chuàng)建序列琢锋,2訂閱序列辕漂,3.發(fā)送信號。當創(chuàng)建序列吴超,并訂閱了序列后钉嘹,只要某個事件發(fā)送了序列消息,就可以在序列訂閱的閉包里面監(jiān)聽到發(fā)送的消息鲸阻。
下面創(chuàng)建一個可觀察序列來感受一下:
//第一步:創(chuàng)建序列
//在create()函數(shù)中傳入一個閉包跋涣,任務(wù)是對每一個過來的訂閱進行處理
let ob = Observable<Any>.create { (observer) -> Disposable in
// 第三步:發(fā)送信號(onCompleted和onError只能發(fā)送一個)
observer.onNext("你好騷啊")
observer.onCompleted()
// observer.onError(NSError.init(domain: "loser", code: 10010, userInfo: nil))
return Disposables.create()
//第二步:訂閱信息
//當我們訂閱了Observable的消息后,只要Observable的事件觸發(fā)鸟悴,都會通過onNext這個閉包告訴我們陈辱。
let _ = ob.subscribe(onNext: { (text) in
print("訂閱到:\(text)") //這里會監(jiān)聽到訂閱的Observable事件
}, onError: { (error) in
print("error: \(error)") //當發(fā)生錯誤時,會回調(diào)這里
}, onCompleted: { // 當序列執(zhí)行完畢時细诸,會回調(diào)這里沛贪。
print("完成")
}) {
print("銷毀")
}
.disposed(by: disposeBag)
DisposeBag
:作用是Rx
在視圖控制器或者其持有者將要銷毀的時候,自動釋法掉綁定在它上面的資源震贵。它是通過類似“訂閱處置機制”方式實現(xiàn)(類似于NotificationCenter
的removeObserver
)利赋。
這就算是RxSwift
響應(yīng)式的核心邏輯了,這里有點疑惑就是:為什么觀察者發(fā)出的信號,可觀察序列能夠訂閱到呢?
未完待續(xù)......
注:感謝下面的參考文檔