Rxswift(一)函數(shù)響應(yīng)式編程思想

@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>
  1. 特點(diǎn):方法的返回值必須是方法的調(diào)用者
  2. 鏈?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;

  1. 此外我們還可以將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)式編程

  1. 響應(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ù)式編程思想為用


    在這里插入圖片描述
  1. 函數(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)行汛骂,非常靈活煎源。

  1. 響應(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):

  1. 創(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>
}
  1. 保存序列到對(duì)象中
    根據(jù)這個(gè)函數(shù)的官方注釋?zhuān)覀兊弥猚reate()這個(gè)方法是在Create.swift中實(shí)現(xiàn)的


    在這里插入圖片描述
  2. 接著我們訂閱消息
    當(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的

  1. 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ǔ)和處理事件的閉包棍弄。

  1. 然后在最后有self.asObservable().subscribe(observer)這樣一行代碼望薄,asObservable返回的是對(duì)象本身。
  2. 然后調(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è)方法蛮原。
  3. 在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è)橋梁的作用。

  1. 最后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ì)象。

  1. 接著分析下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)建的序列。

  1. 分析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)
    }
  1. 通過(guò)一張圖分析上面5的源碼邏輯
在這里插入圖片描述
  1. 到此我們可以弄明白的最初的create()函數(shù)里面的observer是怎么調(diào)用到ob.subscribe()的纸淮。

最后有兩張圖總結(jié)序列事件傳遞的核心流程

在這里插入圖片描述

在這里插入圖片描述

參考文檔:http://t.swift.gg/d/2-rxswift?nojs=1

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末平斩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子萎馅,更是在濱河造成了極大的恐慌双戳,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件糜芳,死亡現(xiàn)場(chǎng)離奇詭異飒货,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)峭竣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)塘辅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人皆撩,你說(shuō)我怎么就攤上這事扣墩≌芤” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵呻惕,是天一觀的道長(zhǎng)荆责。 經(jīng)常有香客問(wèn)我,道長(zhǎng)亚脆,這世上最難降的妖魔是什么做院? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮濒持,結(jié)果婚禮上键耕,老公的妹妹穿的比我還像新娘。我一直安慰自己柑营,他們只是感情好屈雄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著官套,像睡著了一般酒奶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上虏杰,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天讥蟆,我揣著相機(jī)與錄音,去河邊找鬼纺阔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛修然,可吹牛的內(nèi)容都是我干的笛钝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼愕宋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼玻靡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起中贝,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤囤捻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后邻寿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蝎土,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年绣否,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了誊涯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蒜撮,死狀恐怖暴构,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤取逾,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布耗绿,位于F島的核電站,受9級(jí)特大地震影響砾隅,放射性物質(zhì)發(fā)生泄漏误阻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一琉用、第九天 我趴在偏房一處隱蔽的房頂上張望堕绩。 院中可真熱鬧,春花似錦邑时、人聲如沸奴紧。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)黍氮。三九已至,卻和暖如春浅浮,著一層夾襖步出監(jiān)牢的瞬間沫浆,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工滚秩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留专执,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓郁油,卻偏偏與公主長(zhǎng)得像本股,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子桐腌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容