@TOC
1. 函數(shù)響應(yīng)式編程思想必備基本概念簡(jiǎn)介
- 函數(shù)式編程
在計(jì)算機(jī)科學(xué)里展蒂,函數(shù)式編程是一種編程范式晋涣,它將計(jì)算描述為表達(dá)式求值并避免了狀態(tài)和數(shù)據(jù)改變伙菊。函數(shù)式編程里面的“函數(shù)”指的是數(shù)學(xué)函數(shù)迫像。
函數(shù)式編程思想:是將操作盡可能寫(xiě)在一起!嵌套的函數(shù)!!
本質(zhì):就是往方法里面?zhèn)魅隑lock,方法中嵌套Block調(diào)用.
了解函數(shù)式編程首先要理解命令式編程,命令式編程往往是我們大多數(shù)人固有的編程思維梧税,這中模式依賴于我們希望自己的程序如何完成某項(xiàng)任務(wù)沦疾,而與之對(duì)應(yīng)的是聲明式編程(Declarative Programming)称近,它關(guān)心是任務(wù)的目的是什么第队,而不是具體如何實(shí)現(xiàn)。這把程序員從那些細(xì)枝末節(jié)中解放了出來(lái)刨秆。而函數(shù)響應(yīng)式編程正是聲明式編程的一個(gè)子范式凳谦。
-
編程函數(shù)和數(shù)學(xué)函數(shù)對(duì)比
在這里插入圖片描述
數(shù)學(xué)函數(shù)的特點(diǎn)是對(duì)于每一個(gè)自變量,存在唯一的因變量與之對(duì)應(yīng)衡未。而編程函數(shù)的特點(diǎn)是參數(shù)和返回值都不是必須的尸执,函數(shù)可能依賴外界或者影響外界。
命令式和函數(shù)式的區(qū)別
所有的命令式語(yǔ)言都被設(shè)計(jì)來(lái)高效地使用馮諾依曼體系結(jié)構(gòu)的計(jì)算機(jī)缓醋。實(shí)際上如失,最初的命令式語(yǔ)言的目的就是取代匯編語(yǔ)言,對(duì)機(jī)器指令進(jìn)行進(jìn)一步抽象送粱。因此褪贵,命令式語(yǔ)言帶有強(qiáng)烈的硬件結(jié)構(gòu)特征。命令式語(yǔ)言的核心特性有:模擬存儲(chǔ)單元的變量、基于傳輸操作的賦值語(yǔ)句脆丁,以及迭代形式的循環(huán)運(yùn)算世舰。命令式語(yǔ)言的基礎(chǔ)是語(yǔ)句(特別是賦值),它們通過(guò)修改存儲(chǔ)器的值而產(chǎn)生副作用(side effect)的方式去影響后續(xù)的計(jì)算槽卫。
函數(shù)式語(yǔ)言設(shè)計(jì)的基礎(chǔ)是Lambda表達(dá)式跟压,函數(shù)式程序設(shè)計(jì)把程序的輸出定義為其輸入的一個(gè)數(shù)學(xué)函數(shù),在這里沒(méi)有內(nèi)部狀態(tài)歼培,也沒(méi)有副作用震蒋。函數(shù)式語(yǔ)言進(jìn)行計(jì)算的主要是將函數(shù)作用與給定參數(shù)之上。函數(shù)式語(yǔ)言沒(méi)有命令式語(yǔ)言所必需的那種變量躲庄,可以沒(méi)有賦值語(yǔ)句喷好,也可以沒(méi)有循環(huán)。一個(gè)程序就是函數(shù)定義和函數(shù)應(yīng)用的說(shuō)明读跷;一個(gè)程序的執(zhí)行就是對(duì)函數(shù)應(yīng)用的求值梗搅。
詳細(xì)教程參考:理解函數(shù)式編程
- block
block可以作為對(duì)象的屬性,也可以作為方法的參數(shù)效览,也可以作為返回值无切。而作為返回值是鏈?zhǔn)骄幊痰暮诵摹?br> 常用的block用法如下:
1. block表達(dá)式語(yǔ)法:
^返回值類(lèi)型(參數(shù)列表){表達(dá)式}
實(shí)例 1:
^int (int count) {
return count + 1;
};
2. 聲明類(lèi)型變量的語(yǔ)法
//返回值類(lèi)型(^變量名)(參數(shù)列表) = block表達(dá)式
實(shí)例 2:
int (^sum)(int count) = (^int count) {
return count + 1;
};
3. 作為函數(shù)參數(shù)的語(yǔ)法
- (void)func(int (^)(int))block {
}
//定義block簡(jiǎn)寫(xiě)
typedef int (^Sumblock)(int);
- (void)func(Sumblock)block {
}
- 高階函數(shù)
高階函數(shù)實(shí)際上就是函數(shù)的函數(shù),它是所有函數(shù)式語(yǔ)言的性質(zhì)丐枉。函數(shù)式語(yǔ)言中哆键,函數(shù)作為第一等公民,這也意味著你像定義或調(diào)用變量一樣去定義或調(diào)用函數(shù)瘦锹〖冢可以在任何地方定義,在函數(shù)內(nèi)或函數(shù)外弯院,可以將函數(shù)作為參數(shù)或者返回值辱士。在數(shù)學(xué)和計(jì)算機(jī)科學(xué)中,高階函數(shù)是至少滿足下列一個(gè)條件的函數(shù):
1 接受一個(gè)或多個(gè)函數(shù)作為輸入
2 輸出一個(gè)函數(shù)
在數(shù)學(xué)中它們也叫做算子(運(yùn)算符)或泛函听绳。微積分中的導(dǎo)數(shù)就是常見(jiàn)的例子颂碘,因?yàn)樗成湟粋€(gè)函數(shù)到另一個(gè)函數(shù)。
關(guān)于Swift的相關(guān)高階函數(shù)可以參考我的一篇博客:Swift的高階函數(shù)
- 閉包
相關(guān)閉包知識(shí)總結(jié)請(qǐng)參考我的一篇博客:IOS基礎(chǔ)-閉包
2. iOS中三種編程思想:鏈?zhǔn)揭握酢⒑瘮?shù)式和響應(yīng)式編程
2.1 鏈?zhǔn)骄幊?/h2>
- 特點(diǎn):方法的返回值必須是方法的調(diào)用者
- 鏈?zhǔn)綄?xiě)法對(duì)比普通寫(xiě)法
- 普通寫(xiě)法:
實(shí)例 3
@interface Person : NSObject
- (void)eat;
- (void)sleep;
@end
@implementation Person
- (void)eat
{
NSLog(@"%s", __FUNCTION__);
}
- (void)sleep
{
NSLog(@"%s", __FUNCTION__);
}
@end
ViewController.m
Person *person = [[Person alloc] init];
//調(diào)用時(shí)必須單個(gè)調(diào)用头岔,而且不能任意組合順序
/** 普通的調(diào)用方式 */
[person eat];
[person sleep];
- 鏈?zhǔn)綄?xiě)法:
將上面的普通寫(xiě)法改為鏈?zhǔn)剑椒ㄔ黾右粋€(gè)返回值鼠证,且返回值為調(diào)用者本身峡竣。代碼如下:
實(shí)例 4
// 鏈?zhǔn)綄?xiě)法
Person.h
- (Person *)eat;
- (Person *)sleep;
Person.m
- (Person *)eat
{
NSLog(@"%s", __FUNCTION__);
return self;
}
- (Person *)sleep
{
NSLog(@"%s", __FUNCTION__);
return self;
}
ViewController.m
Person *person = [[Person alloc] init];
/** 鏈?zhǔn)綄?xiě)法,這樣不僅可以無(wú)限調(diào)用,而且可以控制順序 */
[[person eat] sleep];
[[person sleep] eat];
[[person eat] eat];
/** 通過(guò)”點(diǎn)”語(yǔ)法量九,將需要執(zhí)行的代碼塊連續(xù)的書(shū)寫(xiě)下去,就是鏈?zhǔn)骄幊?它能使代碼簡(jiǎn)單易讀适掰,書(shū)寫(xiě)方便 */
person.eat.sleep.eat.sleep.sleep;
- 此外我們還可以將blockly作為返回值---鏈?zhǔn)骄幊處?shù)
代碼如下:
實(shí)例 5
Person.h
- (Person *(^)(NSString *food))eat3;
- (Person *(^)(NSString *where))sleep3;
Person.m
- (Person *(^)(NSString *food))eat3
{
return ^(NSString *food) {
NSLog(@"吃:%@ ",food);
return self;
};
}
- (Person *(^)(NSString *where))sleep3
{
return ^(NSString *where) {
NSLog(@"睡在:%@上",where);
return self;
};
}
ViewController.m
Person *person = [[Person alloc] init];
/** 鏈?zhǔn)?+ 函數(shù)式寫(xiě)法 */
person.sleep3(@"床").eat3(@"蘋(píng)果").eat3(@"香蕉").sleep3(@"沙發(fā)");
返回值block不帶參數(shù),()不傳參即可
person.sleep3().eat3().eat3().sleep3();
2.2 函數(shù)式編程
函數(shù)式編程思想:是將操作盡可能寫(xiě)在一起!嵌套的函數(shù)!!
本質(zhì):就是往方法里面?zhèn)魅隑lock,方法中嵌套Block調(diào)用.
如下代碼:
實(shí)例 6
/** 返回調(diào)用者本身,獲取其它屬性和方法 */
- (Person *)calculator:(NSInteger(^)(NSInteger result))block
{
_result = block(_result);
return self;
}
Person *person = [[Person alloc] init];
/** 計(jì)算器 */
Person *calculatPerson = [person calculator:^NSInteger(NSInteger result) {
result = result + 10;
result = result*10;
return result;
}];
NSLog(@"%ld", calculatPerson.result);
簡(jiǎn)單理解就是將block塊作為函數(shù)的參數(shù)攻谁,可以異步在參數(shù)block回調(diào)稚伍,將函數(shù)運(yùn)行所需要的一些信息或者產(chǎn)出的一些結(jié)果通過(guò)block傳遞。代碼邏輯清晰戚宦。
2.3 響應(yīng)式編程
- 響應(yīng)式編程解決的痛點(diǎn):
在程序開(kāi)發(fā)中:a = b + c賦值之后 b 或者 c 的值變化后个曙,a 的值不會(huì)跟著變化;如果我們有這么一個(gè)需求:如果 b 或者 c 的數(shù)值發(fā)生變化受楼,a 的數(shù)值會(huì)同時(shí)發(fā)生變化垦搬。怎么實(shí)現(xiàn)呢?
響應(yīng)式編程就是為這個(gè)而生的艳汽。如我們經(jīng)典的KVO(詳情可以參考我的一篇博客 KVO 底層實(shí)現(xiàn)原理)猴贰,經(jīng)典的響應(yīng)式編程框架RAC。當(dāng)然少不了我們的主角Rxswift.
列如:
實(shí)例 7
a = 2
b = 2
c = a + b // c is 4
b = 3
// now what is the value of c?
在響應(yīng)式編程中河狐,c的值將會(huì)隨著b的值改變而改變米绕。
FRP提供了一種信號(hào)機(jī)制來(lái)實(shí)現(xiàn)這樣的效果,通過(guò)信號(hào)來(lái)記錄值的變化馋艺。信號(hào)可以被疊加栅干、分割或合并。通過(guò)對(duì)信號(hào)的組合捐祠,就不需要去監(jiān)聽(tīng)某個(gè)值或事件.
如下圖:
可以把信號(hào)想象成水龍頭碱鳞,只不過(guò)里面不是水,而是玻璃球(value)踱蛀,直徑跟水管的內(nèi)徑一樣窿给,這樣就能保證玻璃球是依次排列,不會(huì)出現(xiàn)并排的情況(數(shù)據(jù)都是線性處理的率拒,不會(huì)出現(xiàn)并發(fā)情況)崩泡。水龍頭的開(kāi)關(guān)默認(rèn)是關(guān)的,除非有了接收方(subscriber)俏橘,才會(huì)打開(kāi)允华。這樣只要有新的玻璃球進(jìn)來(lái),就會(huì)自動(dòng)傳送給接收方寥掐∩氚瑁可以在水龍頭上加一個(gè)過(guò)濾嘴(filter)费奸,不符合的不讓通過(guò),也可以加一個(gè)改動(dòng)裝置蚕脏,把球改變成符合自己的需求(map)褐隆。也可以把多個(gè)水龍頭合并成一個(gè)新的水龍頭(combineLatest:reduce:)污它,這樣只要其中的一個(gè)水龍頭有玻璃球出來(lái),這個(gè)新合并的水龍頭就會(huì)得到這個(gè)球。
3. 什么是函數(shù)響應(yīng)式編程
什么是函數(shù)響應(yīng)式編程呢衫贬? 可以一句話概括:
-
響應(yīng)式思想為體德澈,函數(shù)式編程思想為用
在這里插入圖片描述
- 函數(shù)式
特點(diǎn):代碼簡(jiǎn)潔(復(fù)用)、易于理解(接近自然語(yǔ)言)固惯、便于代碼管理
例如:doSomething1().doSomething2().doSomething3()
一系列的動(dòng)作簡(jiǎn)潔明了梆造,相互不干擾,可以合并使用也可以分開(kāi)單獨(dú)使用.
實(shí)例 8:下面代碼清楚的講解了函數(shù)式編程與普通處理方式的區(qū)別葬毫。
//例1:
//遍歷數(shù)組(要求:1.首先獲取 > 3的數(shù)字镇辉;2.獲取的數(shù)字之后 + 1;3.所有數(shù)字中的偶數(shù)贴捡;4.可讀性 清晰度)
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)
}
}
}
//函數(shù)式:
array.filter{ $0 > 3}
.filter{ ($0+1) % 2 == 0 }
.forEach { print($0) }
從代碼中我們可以明顯的對(duì)比出代碼的優(yōu)劣忽肛,普通代碼實(shí)現(xiàn)for循環(huán)需要層層嵌套,非常累贅烂斋,可讀性不高屹逛;而利用高階函數(shù)實(shí)現(xiàn)的函數(shù)式編碼代碼清晰簡(jiǎn)潔。你可以注釋掉中間的一個(gè).filter{ ($0+1) % 2 == 0 }
代碼一樣可以正常運(yùn)行汛骂,非常靈活煎源。
- 響應(yīng)式
對(duì)象對(duì)某一數(shù)據(jù)流變化做出響應(yīng)的這種編碼方式稱(chēng)為響應(yīng)式。如對(duì)象A和對(duì)象B香缺,A和B有一種“說(shuō)不清”的關(guān)系手销,A要時(shí)刻監(jiān)控B的行為,對(duì)B的變化也做出相應(yīng)的變化图张。那么怎么實(shí)現(xiàn)呢锋拖?對(duì)于B來(lái)說(shuō),B做任何事都需要向A匯報(bào)祸轮,這樣A就能實(shí)時(shí)監(jiān)控B的行為兽埃,并響應(yīng)。在iOS開(kāi)發(fā)中我們經(jīng)常會(huì)響應(yīng)一些事件适袜,button柄错、tap、textField苦酱、textView售貌、notifaction、KVO疫萤、NSTimer等等這些颂跨,都需要做響應(yīng)監(jiān)聽(tīng),響應(yīng)后都需要在對(duì)應(yīng)的響應(yīng)事件中去做處理扯饶,而原生開(kāi)發(fā)中恒削,觸發(fā)對(duì)象與響應(yīng)方法是分離的池颈,如button的初始化和點(diǎn)擊響應(yīng)方法是分離的。
在程序開(kāi)發(fā)中:a = b + c賦值之后 b 或者 c 的值變化后钓丰,a 的值不會(huì)跟著變化躯砰;
響應(yīng)式編程目標(biāo)就是:如果 b 或者 c 的數(shù)值發(fā)生變化,a 的數(shù)值會(huì)同時(shí)發(fā)生變化携丁;
響應(yīng)編程的經(jīng)典案例:KVO
響應(yīng)式編程框架:ReactiveCocoa(RAC) 詳細(xì)學(xué)習(xí)可以參考:ReactiveCocoa博客
響應(yīng)式編程的優(yōu)點(diǎn):
1) 開(kāi)發(fā)過(guò)程中琢歇,狀態(tài)以及狀態(tài)之間依賴過(guò)多,RAC更加有效率地處理事件流,而無(wú)需顯式去管理狀態(tài)则北。在OO或者過(guò)程式編程中矿微,狀態(tài)變化是最難跟蹤,最頭痛的事尚揣。這個(gè)也是最重要的一點(diǎn)涌矢。
2) 減少變量的使用,由于它跟蹤狀態(tài)和值的變化快骗,因此不需要再申明變量不斷地觀察狀態(tài)和更新值娜庇。
3) 提供統(tǒng)一的消息傳遞機(jī)制,將oc中的通知方篮,action名秀,KVO以及其它所有UIControl事件的變化都進(jìn)行監(jiān)控,當(dāng)變化發(fā)生時(shí)藕溅,就會(huì)傳遞事件和值匕得。
4) 當(dāng)值隨著事件變換時(shí),可以使用map巾表,filter汁掠,reduce等函數(shù)便利地對(duì)值進(jìn)行變換操作。
下面統(tǒng)一對(duì)比Rxswift的UIButton事件實(shí)現(xiàn)和原生的實(shí)現(xiàn)方式集币,了解一下響應(yīng)式編程的優(yōu)點(diǎn)考阱。
實(shí)例 9 :對(duì)比傳統(tǒng)UIButton響應(yīng)事件寫(xiě)法和Rxswift中函數(shù)響應(yīng)式寫(xiě)法的區(qū)別。
1. 傳統(tǒng)寫(xiě)法
//傳統(tǒng)寫(xiě)法鞠苟,UI代碼和邏輯是分開(kāi)的乞榨,為了監(jiān)聽(tīng)一個(gè)按鈕的響應(yīng)事件,我們需要在另外一處地方實(shí)現(xiàn)当娱。這樣可讀性不好吃既,代碼繁瑣。
button = UIButton()
button.addTarget(self, action: #selector(text), for: .touchUpInside)
@objc func text() {
print("Button clicked趾访!")
}
2. Rxswift寫(xiě)法
//Rxswift的實(shí)現(xiàn)就簡(jiǎn)單多了态秧,而且目標(biāo)非常明確,就是三部曲:1創(chuàng)建序列扼鞋,2申鱼,訂閱響應(yīng)消息,3.析構(gòu)銷(xiāo)毀
//當(dāng)你訂閱了響應(yīng)消息后云头,只要序列發(fā)生了變化捐友,訂閱的消息總會(huì)觸發(fā),如下面的代碼溃槐,當(dāng)你訂閱了按鈕的點(diǎn)擊事件后匣砖,每次點(diǎn)擊按鈕,訂閱的消息subscibe就會(huì)收到一次昏滴。
self.button.rx.tap //序列猴鲫,這里默認(rèn)的序列是默認(rèn)是.onTouchUpInside事件
.subscribe(onNext: { () in //訂閱
print("Button clicked!")
}, onError: { (error) in //當(dāng)Rxswift的事件鏈走不通谣殊,會(huì)回調(diào)這個(gè)onError拂共,通知錯(cuò)誤
print("錯(cuò)誤信息")
}, onCompleted: {//當(dāng)Rxswift訂閱的所有事件鏈走完了,會(huì)回調(diào)這個(gè)onCompleted姻几,告知執(zhí)行完畢宜狐,這個(gè)和onError是對(duì)立互斥的,兩者只會(huì)發(fā)生一個(gè)蛇捌。
print("訂閱完成")
})
.disposed(by: DisposeBag()) //銷(xiāo)毀
這里 taps 就是按鈕點(diǎn)擊事件的序列抚恒。然后我們通過(guò)打印“Button clicked!”络拌,來(lái)對(duì)每一次點(diǎn)擊事件做出響應(yīng)俭驮。這種編程方式叫做響應(yīng)式編程。我們結(jié)合函數(shù)式編程以及響應(yīng)式編程就得到了函數(shù)響應(yīng)式編程春贸。通過(guò)不同的構(gòu)建函數(shù)混萝,來(lái)創(chuàng)建所需要的數(shù)據(jù)序列。最后通過(guò)適當(dāng)?shù)姆绞絹?lái)響應(yīng)這個(gè)序列祥诽。這就是函數(shù)響應(yīng)式編程譬圣。
通過(guò)上面的代碼對(duì)比分析,我們可以看出Rxswift寫(xiě)出來(lái)的代碼是多么簡(jiǎn)介雄坪,可讀性邏輯清晰厘熟,看每一行就知道在做什么,不需要想原生UI響應(yīng)和邏輯分開(kāi)的维哈,看代碼需要跳來(lái)跳去的绳姨。通過(guò)一個(gè)這么小小的實(shí)例代碼,我們初次見(jiàn)識(shí)到了Rxswift的強(qiáng)大阔挠,然而這個(gè)只是Rxswift框架功能的冰山一角飘庄。我強(qiáng)烈推薦大家都來(lái)學(xué)習(xí)下這么優(yōu)秀的開(kāi)源框架。
下面我們來(lái)窺探一下Rxswift到底是個(gè)什么東東购撼,為啥這么牛逼跪削。
4. Rxswift簡(jiǎn)介
4.1 什么是 ReactiveX(Reactive Extensions)
An API for asynchronous programming with observable streams
通過(guò)可觀察的流實(shí)現(xiàn)異步編程的一種API(不明白谴仙?嗯,看完所有的例子再讀一篇)
ReactiveX is more than an API, it's an idea and a breakthrough in programming. It has inspired several other APIs, frameworks, and even programming languages.
ReactiveX 不僅僅是一種 API 那么簡(jiǎn)單碾盐,它更是一種編程思想的突破晃跺。它已經(jīng)影響了其他 API,frameworks毫玖,以及編程語(yǔ)言掀虎。
總的一句話概括:
ReactiveX(Reactive Extensions)是通過(guò)可觀察的流實(shí)現(xiàn)異步編程的一種API,它結(jié)合了觀察者模式付枫、迭代器模式和函數(shù)式編程的精華烹玉。RxSwift 是 ReactiveX 編程思想的一種實(shí)現(xiàn),幾乎每一種語(yǔ)言都會(huì)有那么一個(gè) Rx[xxxx] 框架阐滩,比如Rxswift, RxJava二打,RxJS 等等。
4.2 Rx的基本概念
Rx是一個(gè)家族叶眉,他們把函數(shù)響應(yīng)式編程思想使用到了極致址儒,要學(xué)習(xí)Rxswift必須先了解一下一些基本概念。
- 觀察者模式 Observable:
對(duì)某些數(shù)據(jù)流(很廣衅疙,可以是一些事件等)進(jìn)行處理莲趣,使其變成可觀察對(duì)象(Observable)序列,這樣觀察者(observer)就可以訂閱這些序列饱溢;
Observable 直譯為可觀察的喧伞,它在 RxSwift 起到了舉足輕重的作用,在整個(gè) RxSwift 的使用過(guò)程中你會(huì)經(jīng)常與它打交道绩郎。如果你使用過(guò) RAC 潘鲫,它如同 Signal 一樣。RxSwift 中關(guān)鍵點(diǎn)就是在于如何把普通的數(shù)據(jù)或者事件變成可觀察的肋杖,這樣當(dāng)某些數(shù)據(jù)或事件有變化的時(shí)候就會(huì)通知它的訂閱者溉仑。RxSwift 中提供很多種創(chuàng)建 Observable 創(chuàng)建方法。比如:From状植、never浊竟、empty 和 create 等,更多創(chuàng)建方法津畸。訂閱者可以收到 3 個(gè)事件振定,onNext、onError 和 onCompleted肉拓,每個(gè) Observable 都應(yīng)該至少有一個(gè) onError 或 onCompleted 事件后频,onNext 表示它傳給下一個(gè)接收者時(shí)的數(shù)據(jù)流。
在Rxswift中我們把它理解為:觀察者序列暖途,(就是一系列可以被監(jiān)聽(tīng)卑惜,觀察的事件等膏执,當(dāng)你訂閱了這些序列,你就可以在閉包中監(jiān)聽(tīng)到對(duì)應(yīng)的事件)通過(guò)下面一個(gè)圖可以更好的理解Observable:
序列監(jiān)聽(tīng)有三個(gè)步驟:1.創(chuàng)建序列残揉,2訂閱序列胧后,3.銷(xiāo)毀序列芋浮。當(dāng)創(chuàng)建序列抱环,并訂閱了序列后,只要某個(gè)事件發(fā)送了序列消息纸巷,就可以在訂閱的閉包里面監(jiān)聽(tīng)到镇草。下面我們一個(gè)小的實(shí)例來(lái)加深理解:
實(shí)例 10
//1:創(chuàng)建序列
//利用函數(shù)式編程思想,在create()構(gòu)造函數(shù)中傳入一個(gè)閉包瘤旨,這個(gè)閉包會(huì)被類(lèi)對(duì)象保存起來(lái)梯啤,后續(xù)每個(gè)時(shí)間,事件觸發(fā)的時(shí)候會(huì)回調(diào)這個(gè)傳入的閉包存哲,這樣就像連接了一個(gè)鏈條一樣因宇,順著鏈條就可找到需要調(diào)用的閉包。
let ob = Observable<Any>.create { (observer) -> Disposable in
// 3:發(fā)送信號(hào)
obserber.onNext([1,2,3,4])
obserber.onCompleted()
// obserber.onError(NSError.init(domain: "error祟偷!", code: 10087, userInfo: nil))
return Disposables.create()
//2:訂閱信息
//當(dāng)我們訂閱了Observable的消息后察滑,只要Observable的事件觸發(fā),都會(huì)通過(guò)OnNext這個(gè)閉包告訴我們修肠。
let _ = ob.subscribe(onNext: { (text) in
print("訂閱到:\(text)") //這里會(huì)監(jiān)聽(tīng)到訂閱的Observable消息
}, onError: { (error) in
print("error: \(error)") //當(dāng)發(fā)生錯(cuò)誤時(shí)贺辰,會(huì)回調(diào)這里
}, onCompleted: { // 當(dāng)所有序列執(zhí)行完畢時(shí),會(huì)回調(diào)這里嵌施。
print("完成")
}) {
print("銷(xiāo)毀")
}
如果你仔細(xì)觀察這里的代碼饲化,會(huì)有一個(gè)疑問(wèn):從訂閱中心observer,一直在用的序列吗伤,序列內(nèi)部的代碼是不曾看到的吃靠。為什么從序列閉包里面的發(fā)出信號(hào),訂閱信號(hào)的閉包里面能夠訂閱到足淆? 這個(gè)問(wèn)題我們將會(huì)在Rxswift 序列核心邏輯淺析
中詳細(xì)分析
- 操作符 Operators:
然而對(duì)于訂閱者來(lái)說(shuō)(observer)某些選項(xiàng)(items)并不是自己需要的(需要過(guò)濾)巢块,某些選項(xiàng)(items)需要轉(zhuǎn)換才能達(dá)到自己的目的;
Observable 創(chuàng)建后缸浦,可能為了滿足某些需求需要修改它夕冲,這時(shí)就需要用到操作符。RxSwift 提供了非常多的操作符裂逐,當(dāng)然不必要一一掌握這些操作符歹鱼,使用的時(shí)候查一下即可,當(dāng)然常見(jiàn)的操作符必須要掌握卜高,比如 map弥姻、flatMap 南片、create 、filter 等庭敦。Operators詳細(xì)介紹
實(shí)例 11:
這個(gè)例子主要把查找數(shù)組中的字符串 kongyulu疼进,并顯示到 Label 上。
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global().async {
self.from()
}
}
func from() {
Observable.from(["haha", "kongyulu", "cc", "wswy", "Rx"])
.subscribeOn(MainScheduler.instance)
.filter({ (text) -> Bool in
return text == "kongyulu"
})
.map({ (text) -> String in
return "my name is: " + text
})
.subscribe(onNext: { [weak self] (text) in
self?.nickNameLabel.text = text
})
.disposed(by: disposeBag)
}
迭代模式 Iterator:
這樣集合或者序列中的值就可以進(jìn)行遍歷了秧廉。調(diào)度器 Scheduler:
為了提升用戶體驗(yàn)伞广,或其它目的,有些操作需要放到特定的線程去執(zhí)行疼电,比如 UI 操作需要放到主線程嚼锄,這就涉及到了調(diào)度器。
如果你想給 Observable 操作符鏈添加多線程功能蔽豺,你可以指定操作符(或者特定的Observable)在特定的調(diào)度器(Scheduler)上執(zhí)行区丑。對(duì)于 ReactiveX 中可觀察對(duì)象操作符來(lái)說(shuō),它有時(shí)會(huì)攜帶一個(gè)調(diào)度器作為參數(shù)修陡,這樣可以指定可觀察對(duì)象在哪一個(gè)線程中執(zhí)行沧侥。而默認(rèn)的情況下,某些可觀察對(duì)象是在訂閱者訂閱時(shí)的那個(gè)線程中執(zhí)行魄鸦。SubscribeOn 可以改變可觀察對(duì)象該在那個(gè)調(diào)度器中執(zhí)行宴杀。ObserveOn 用來(lái)改變給訂閱者發(fā)送通知時(shí)所在的調(diào)度器。這樣就可以使可觀察對(duì)象想在那個(gè)調(diào)度器中執(zhí)行就在那個(gè)調(diào)度器中執(zhí)行号杏,不受約束婴氮,而這些細(xì)節(jié)是不被調(diào)用者所關(guān)心的。猶如 GCD 一樣盾致,你只管使用主经,底層線程是咋么創(chuàng)建的,你不必關(guān)心庭惜。
4.3 Rxswift框架的優(yōu)點(diǎn)
4.4 Rxswift框架安裝
Rxswift就是一個(gè)框架而已罩驻,通過(guò)pod安裝跟其他框架沒(méi)有什么差異
在podfile中寫(xiě)入
pod 'RxSwift'
pod 'RxCocoa'
命令行執(zhí)行pod install
就可以了。
4.5 Rxswift簡(jiǎn)單使用
5. Rxswift 序列核心邏輯淺析
在實(shí)例 10
中我們留下一個(gè)疑問(wèn):從訂閱中心observer护赊,一直在用的序列惠遏,序列內(nèi)部的代碼是不曾看到的。為什么從序列閉包里面的發(fā)出信號(hào)骏啰,訂閱信號(hào)的閉包里面能夠訂閱到节吮?
下面我們將一步步通過(guò)Rxswift的源碼分析來(lái)揭開(kāi)這個(gè)疑團(tuán)。
在分析代碼前我們先回顧一下序列的三部曲:1.創(chuàng)建序列判耕,2透绩,訂閱序列,3,銷(xiāo)毀序列帚豪,其中在2中訂閱序列之后碳竟,為什么我們就能監(jiān)聽(tīng)到序列呢,理解了這個(gè)邏輯后狸臣,我們的疑問(wèn)就會(huì)自然而解莹桅。
先看一個(gè)簡(jiǎn)單的類(lèi)圖:
接著我們來(lái)研究一下這段代碼的執(zhí)行邏輯
1:創(chuàng)建序列
// AnonymousObservable -> producer.subscriber -> run
// 保存閉包 - 函數(shù)式 保存 _subscribeHandler
//
let ob = Observable<Any>.create { (obserber) -> Disposable in
// 3:發(fā)送信號(hào)
obserber.onNext("框架班級(jí)")
obserber.onCompleted()
// obserber.onError(NSError.init(domain: "coocieeror", code: 10087, userInfo: nil))
return Disposables.create()
}
2:訂閱信號(hào)
// AnonymousObserver - event .next -> onNext()
// _eventHandler
// AnonymousObservable._subscribeHandler(observer)
// 銷(xiāo)毀
let _ = ob.subscribe(onNext: { (text) in
print("訂閱到:\(text)")
}, onError: { (error) in
print("error: \(error)")
}, onCompleted: {
print("完成")
}) {
print("銷(xiāo)毀")
}
用一個(gè)圖來(lái)表達(dá)上面這段代碼執(zhí)行時(shí),Rxswift框架做的事情如下:
現(xiàn)在我們來(lái)研究一下源碼的具體實(shí)現(xiàn):
- 創(chuàng)建序列
當(dāng)我們調(diào)用let ob = Observable<Any>.create { (obserber) -> Disposable in }
這行代碼時(shí)烛亦,進(jìn)入Rxswift源碼可以看到實(shí)際調(diào)用了:
extension ObservableType {
/**
Creates an observable sequence from a specified subscribe method implementation.
- seealso: [create operator on reactivex.io](http://reactivex.io/documentation/operators/create.html)
- parameter subscribe: Implementation of the resulting observable sequence's `subscribe` method.
- returns: The observable sequence with the specified implementation for the `subscribe` method.
*/
public static func create(_ subscribe: @escaping (RxSwift.AnyObserver<Self.E>) -> Disposable) -> RxSwift.Observable<Self.E>
}
-
保存序列到對(duì)象中
根據(jù)這個(gè)函數(shù)的官方注釋?zhuān)覀兊弥猚reate()這個(gè)方法是在Create.swift中實(shí)現(xiàn)的
在這里插入圖片描述 - 接著我們訂閱消息
當(dāng)我執(zhí)行let _ = ob.subscribe(onNext: { (text) }, onError: { (error) in }, onCompleted: { })
這行代碼時(shí)诈泼,我們就訂閱了消息,我們分析源碼得知此洲,Rxswift實(shí)際上是調(diào)用了
在這里插入圖片描述
我們進(jìn)入源碼查看該方法的實(shí)現(xiàn):
/**
Subscribes an element handler, an error handler, a completion handler and disposed handler to an observable sequence.
- parameter onNext: Action to invoke for each element in the observable sequence.
- parameter onError: Action to invoke upon errored termination of the observable sequence.
- parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
- parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has
gracefully completed, errored, or if the generation is canceled by disposing subscription).
- returns: Subscription object used to unsubscribe from the observable sequence.
*/
public func subscribe(onNext: ((E) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)
-> Disposable {
let disposable: Disposable
if let disposed = onDisposed {
disposable = Disposables.create(with: disposed)
}
else {
disposable = Disposables.create()
}
#if DEBUG
let synchronizationTracker = SynchronizationTracker()
#endif
let callStack = Hooks.recordCallStackOnError ? Hooks.customCaptureSubscriptionCallstack() : []
let observer = AnonymousObserver<E> { event in
#if DEBUG
synchronizationTracker.register(synchronizationErrorMessage: .default)
defer { synchronizationTracker.unregister() }
#endif
switch event {
case .next(let value):
onNext?(value)
case .error(let error):
if let onError = onError {
onError(error)
}
else {
Hooks.defaultErrorHandler(callStack, error)
}
disposable.dispose()
case .completed:
onCompleted?()
disposable.dispose()
}
}
return Disposables.create(
self.asObservable().subscribe(observer),
disposable
)
}
分析源碼厂汗,可以看到,我們?cè)谟嗛喯⒆?cè)的回調(diào):onNext, onError,onComplete, onDisposed這個(gè)四個(gè)閉包都是作為函數(shù)的參數(shù)呜师,在調(diào)用ob.subscribe()這個(gè)方法是作為參數(shù)傳入進(jìn)來(lái)的,在上面代碼中定義了一個(gè)逃逸閉包 let observer = AnonymousObserver<E> {} 在這個(gè)閉包內(nèi)部贾节,當(dāng)調(diào)用這個(gè)逃逸閉包的調(diào)用者傳遞不同的event就會(huì)調(diào)用我們傳入的相應(yīng)閉包汁汗,這樣我們就監(jiān)聽(tīng)到了訂閱的消息。如下圖:
這里我們可以得知栗涂,只有我們的觀察者有了事件變化知牌,只需要通知上面代碼定義的observer這個(gè)參數(shù)閉包就可以了。
現(xiàn)在我們前面提到的疑問(wèn)的答案來(lái)了:
在源碼中我們只看到了return Disposables.create( self.asObservable().subscribe(observer), disposable
就結(jié)束了斤程。 然后玄機(jī)就在這句代碼角寸,self.asObservable()其實(shí)就是我們的創(chuàng)建的序列,而subscribe()就回調(diào)了我們傳入的observer閉包忿墅,而在這個(gè)observer就會(huì)調(diào)用我們船人都監(jiān)聽(tīng)序列消息閉包onNext(), onError(),onCompleted().
接下來(lái)我們可以分析下 subscribe(observer)是如何調(diào)用observer的
- subscribe(observer)是如何調(diào)用observer的扁藕?
看看下面這張圖就明白了:
5.1 Rxswift 序列核心邏輯流程總結(jié)
通過(guò)上面的分析,到這里我們總結(jié)一下大致流程:
1.調(diào)用create()創(chuàng)建序列時(shí)疚脐,首先創(chuàng)建了一個(gè)AnonymousObserver對(duì)象亿柑,在初始化時(shí)傳遞了一個(gè)閉包作為參數(shù)并且保存下來(lái)self._eventHandler = eventHandler。
AnonymousObserver是匿名觀察者,用于存儲(chǔ)和處理事件的閉包棍弄。
- 然后在最后有self.asObservable().subscribe(observer)這樣一行代碼望薄,asObservable返回的是對(duì)象本身。
- 然后調(diào)用subscribe這個(gè)函數(shù)并且把創(chuàng)建的AnonymousObserver對(duì)象傳遞過(guò)去呼畸,會(huì)來(lái)到AnonymousObservable這個(gè)類(lèi)里面痕支,但是發(fā)現(xiàn)這個(gè)類(lèi)里面沒(méi)有subscribe方法,我們往父類(lèi)Producer里面找到這個(gè)方法蛮原。
- 在Producer源碼里面我們發(fā)現(xiàn)調(diào)用了run方法卧须,也就是AnonymousObservable這個(gè)類(lèi)里面的run方法,把observer作為參數(shù)傳過(guò)來(lái)。
return CurrentThreadScheduler.instance.schedule(()) { _ in
let disposer = SinkDisposer()
let sinkAndSubscription = self.run(observer, cancel: disposer)
disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription)
return disposer
}
在run方法中故慈,先創(chuàng)建一個(gè)AnonymousObservableSink對(duì)象并持有observer板熊,然后調(diào)用這個(gè)對(duì)象的run方法把self傳遞過(guò)去,也就是把observable作為參數(shù)察绷。
AnonymousObservableSink這個(gè)類(lèi)將可觀察者Observable和觀察者Observer鏈接起來(lái),實(shí)現(xiàn)事件的傳遞干签,起到一個(gè)橋梁的作用。
- 最后Producer父類(lèi)會(huì)調(diào)用子類(lèi)的run()方法,子類(lèi)parent就是我們創(chuàng)建的序列拆撼。
//parent就是我們創(chuàng)建的序列容劳。
func run(_ parent: Parent) -> Disposable {
return parent._subscribeHandler(AnyObserver(self))
}
在這里觸發(fā)了_subscribeHandler的調(diào)用,這里的_subscribeHandler就是之前create函數(shù)參數(shù)傳入的閉包闸度。也就是這段閉包代碼:let ob = Observable<Any>.create { (obserber) -> Disposable in // 3:發(fā)送信號(hào) obserber.onNext("框架班級(jí)") obserber.onCompleted() // obserber.onError(NSError.init(domain: "coocieeror", code: 10087, userInfo: nil)) return Disposables.create() }
注意:parent._subscribeHandler(AnyObserver(self)) 這行代碼把self轉(zhuǎn)換成AnyObserver對(duì)象竭贩,也就是把AnonymousObservableSink對(duì)象轉(zhuǎn)換成AnyObserver對(duì)象。
- 接著分析下AnyObserver源碼莺禁,可以看到在構(gòu)造函數(shù)中有一行代碼self.observer = observer.on留量,就是把AnonymousObservableSink類(lèi)的on函數(shù)賦值給AnyObserver類(lèi)的observer變量。從這里就可以明白為什么這行代碼observer.onNext("發(fā)送信號(hào)") 最終會(huì)觸發(fā)AnonymousObservableSink.on事件哟冬。
public struct AnyObserver<Element> : ObserverType {
...
/// Construct an instance whose `on(event)` calls `observer.on(event)`
///
/// - parameter observer: Observer that receives sequence events.
public init<O : ObserverType>(_ observer: O) where O.E == Element {
self.observer = observer.on
}
/// Send `event` to this observer.
///
/// - parameter event: Event instance.
public func on(_ event: Event<Element>) {
return self.observer(event)
}
/// Erases type of observer and returns canonical observer.
///
/// - returns: type erased observer.
public func asObserver() -> AnyObserver<E> {
return self
}
}
通過(guò)上面這段源碼我們也看見(jiàn)public func asObserver() -> AnyObserver<E> { return self }
這個(gè)函數(shù)返回self 也就明白了之前說(shuō)的 asObserver()為什么就是我們創(chuàng)建的序列楼熄。也就是這個(gè)非常牛逼的代碼return Disposables.create( self.asObservable().subscribe(observer), disposable )
這里的self.asObservable() 實(shí)際就是我們創(chuàng)建的序列。
- 分析AnonymousObservableSink.on事件源碼浩峡,可以得知在收到.error, .completed事件時(shí)可岂,會(huì)調(diào)用forwardOn()方法,而在fowardOn()方法里面會(huì)調(diào)用self._observer.on(event)翰灾。
這里的_observer就是第二步調(diào)用subscribe函數(shù)里面創(chuàng)建的observer對(duì)象缕粹。
會(huì)先進(jìn)入到父類(lèi)的ObserverBase的on方法。
具體源碼如下:
1. AnonymousObservableSink類(lèi)on方法
func on(_ event: Event<E>) {
#if DEBUG
self._synchronizationTracker.register(synchronizationErrorMessage: .default)
defer { self._synchronizationTracker.unregister() }
#endif
switch event {
case .next:
if load(self._isStopped) == 1 {
return
}
self.forwardOn(event)
case .error, .completed:
if fetchOr(self._isStopped, 1) == 0 {
self.forwardOn(event)
self.dispose()
}
}
2. Sink類(lèi)的forwardOn方法
final func forwardOn(_ event: Event<O.E>) {
#if DEBUG
self._synchronizationTracker.register(synchronizationErrorMessage: .default)
defer { self._synchronizationTracker.unregister() }
#endif
if isFlagSet(self._disposed, 1) {
return
}
self._observer.on(event)
}
3. ObserverBase的on方法
func on(_ event: Event<E>) {
switch event {
case .next:
if load(self._isStopped) == 0 {
self.onCore(event)
}
case .error, .completed:
if fetchOr(self._isStopped, 1) == 0 {
self.onCore(event)
}
}
}
4. onCore()方法最終會(huì)調(diào)用之前保存的閉包_eventHandler(event)
override func onCore(_ event: Event<Element>) {
return self._eventHandler(event)
}
- 通過(guò)一張圖分析上面5的源碼邏輯
- 到此我們可以弄明白的最初的create()函數(shù)里面的observer是怎么調(diào)用到ob.subscribe()的纸淮。
最后有兩張圖總結(jié)序列事件傳遞的核心流程