iOS之NSOperation明场、NSOperationQueue

一汽摹、 為什么要使用 NSOperation、NSOperationQueue

NSOperation苦锨、NSOperationQueue 是基于 GCD 更高一層的封裝逼泣,完全面向?qū)ο蟆5潜?GCD 更簡單易用舟舒、代碼可讀性也更高拉庶。
可添加完成的代碼塊,在操作完成后執(zhí)行秃励。
添加操作之間的依賴關(guān)系氏仗,方便的控制執(zhí)行順序。
設(shè)定操作執(zhí)行的優(yōu)先級夺鲜。
可以很方便的取消一個操作的執(zhí)行廓鞠。
使用 KVO 觀察對操作執(zhí)行狀態(tài)的更改:isExecuteing、isFinished谣旁、isCancelled。

二滋早、同步異步以及并行串行的區(qū)別

同步就是指一個進程在執(zhí)行某個請求的時候榄审,若該請求需要一段時間才能返回信息,那么這個進程將會一直等待下去杆麸,直到收到返回信息才繼續(xù)執(zhí)行下去搁进;異步是指進程不需要一直等下去浪感,而是繼續(xù)執(zhí)行下面的操作,不管其他進程的狀態(tài)
并行串行是指有多個線程的任務(wù)的執(zhí)行方式饼问,串行則多個任務(wù)按順序執(zhí)行影兽,并行則多個任務(wù)會同時執(zhí)行

三、 NSOperation 實現(xiàn)多線程的使用步驟分為三步:

NSOperation 需要配合 NSOperationQueue 來實現(xiàn)多線程莱革。因為默認情況下峻堰,NSOperation 單獨使用時系統(tǒng)同步執(zhí)行操作,配合 NSOperationQueue 我們能更好的實現(xiàn)異步執(zhí)行盅视。

1捐名、創(chuàng)建操作:先將需要執(zhí)行的操作封裝到一個NSOperation 對象中。
2闹击、創(chuàng)建隊列:創(chuàng)建NSOperationQueue對象镶蹋。
3、將操作加入到隊列中:將 NSOperation 對象添加到 NSOperationQueue 對象中赏半。

之后呢贺归,系統(tǒng)就會自動將 NSOperationQueue 中的 NSOperation 取出來,在新線程中執(zhí)行操作断箫。

四、線程死鎖

//    NSLog(@"1");
//    dispatch_sync(dispatch_get_main_queue(), ^{
//        NSLog(@"2");
//    });
//    NSLog(@"3");

主線程是串行隊列, 一個任務(wù)執(zhí)行完成才能往下執(zhí)行, 同步線程是一個任務(wù)A ,里面的block是同步線程插入到當(dāng)前線程的另一個任務(wù)B ,A要等B執(zhí)行完才返回 ,B要等A執(zhí)行完才能執(zhí)行 ,會相互等待 造成死鎖

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
//    創(chuàng)建操作
//    [self useInvocationOperation];
////    NSOperation單獨使用 同步執(zhí)行 等待結(jié)果返回后執(zhí)行后面語句
//    NSLog(@"下一個語句");
    
//    [self useBlockOperation];
    
//    創(chuàng)建隊列
//    [self addOperationToQueue];
    
//    添加依賴
//    [self addDependency];
    
//    優(yōu)先級
//    [self queuePriority];
    
//    線程間通訊
//    [self communication];
    
    
//    線程死鎖:主線程是串行隊列 一個任務(wù)執(zhí)行完成才能往下執(zhí)行 同步線程是一個任務(wù)A 里面的block是同步線程插入到當(dāng)前線程的另一個任務(wù)B A要等B執(zhí)行完才返回 B要等A執(zhí)行完才能執(zhí)行 會相互等待 造成死鎖
//    NSLog(@"1");
//    dispatch_sync(dispatch_get_main_queue(), ^{
//        NSLog(@"2");
//    });
//    NSLog(@"3");
}


#pragma mark -  創(chuàng)建操作
//NSOperation 單獨使用時系統(tǒng)同步執(zhí)行操作
//NSOperation 是個抽象類踱葛,不能用來封裝操作。我們只有使用它的子類來封裝操作尸诽。我們有三種方式來封裝操作。
//使用子類 NSInvocationOperation
//使用子類 NSBlockOperation
//自定義繼承自 NSOperation 的子類性含,通過實現(xiàn)內(nèi)部相應(yīng)的方法來封裝操作。
- (void)useInvocationOperation{
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationTask) object:nil];
    
//    開始操作
    [invocationOperation start];
}

//NSBlockOperation 還提供了一個方法 addExecutionBlock:商蕴,通過 addExecutionBlock: 就可以為 NSBlockOperation 添加額外的操作。這些操作(包括 blockOperationWithBlock 中的操作)可以在不同的線程中同時(并發(fā))執(zhí)行(完成順序是不一定的)绪商。只有當(dāng)所有相關(guān)的操作已經(jīng)完成執(zhí)行時,才視為完成

//一般情況下格郁,如果一個 NSBlockOperation 對象封裝了多個操作。NSBlockOperation 是否開啟新線程例书,取決于操作的個數(shù)锣尉。如果添加的操作的個數(shù)多,就會自動開啟新線程决采。當(dāng)然開啟的線程數(shù)是由系統(tǒng)來決定的自沧。
- (void)useBlockOperation{
    NSBlockOperation * blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"blockOperation---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }

    }];
    
    // 2.添加額外的操作
    [blockOperation addExecutionBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    [blockOperation addExecutionBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"3---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    [blockOperation addExecutionBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"4---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    [blockOperation addExecutionBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"5---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    [blockOperation addExecutionBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"6---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    [blockOperation addExecutionBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"7---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    [blockOperation addExecutionBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"8---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
   
    
    
//    調(diào)用 start 方法開始執(zhí)行操作
    [blockOperation start];
}
- (void)useCustomOperation{
    
}

#pragma mark - 創(chuàng)建隊列
//NSOperationQueue 創(chuàng)建的自定義隊列同時具有串行、并發(fā)功能树瞭,通過設(shè)置屬性maxConcurrentOperationCount(最大并發(fā)操作數(shù))用來控制一個特定隊列中可以有多少個操作同時參與并發(fā)執(zhí)行拇厢。
//當(dāng)最大并發(fā)操作數(shù)為1時,操作是按順序串行執(zhí)行的移迫,并且一個操作完成之后旺嬉,下一個操作才開始執(zhí)行。當(dāng)最大操作并發(fā)數(shù)為2時厨埋,操作是并發(fā)執(zhí)行的邪媳,可以同時執(zhí)行兩個操作。而開啟線程數(shù)量是由系統(tǒng)決定的荡陷,不需要我們來管理


//那么我們需要將創(chuàng)建好的操作加入到隊列中去雨效。總共有兩種方法:
//1废赞、- (void)addOperation:(NSOperation *)op;
//2徽龟、- (void)addOperationWithBlock:(void (^)(void))block;

/**
 * 使用 addOperation: 將操作加入到操作隊列中
 */
- (void)addOperationToQueue {
    
    // 1.創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//    設(shè)置最大并發(fā)操作數(shù) 來控制隊列的串行或者并發(fā)
    queue.maxConcurrentOperationCount = 1;
//    queue.maxConcurrentOperationCount = 8;
    // 2.創(chuàng)建操作
    // 使用 NSInvocationOperation 創(chuàng)建操作1
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationTask) object:nil];
    
    // 使用 NSInvocationOperation 創(chuàng)建操作2
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationTask2) object:nil];
    
    op1.queuePriority = NSOperationQueuePriorityHigh;
    op2.queuePriority = NSOperationQueuePriorityLow;
    
    // 使用 NSBlockOperation 創(chuàng)建操作3
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"3---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    [op3 addExecutionBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"4---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    
    // 3.使用 addOperation: 添加所有操作到隊列中
    [queue addOperation:op1]; // [op1 start] 添加到隊列中 無需再自己調(diào)用start
    [queue addOperation:op2]; // [op2 start]
    [queue addOperation:op3]; // [op3 start]
}

/**
 * 使用 addOperationWithBlock: 將操作加入到操作隊列中
 無需自己創(chuàng)建操作對象 直接將操作放到block中
 */

- (void)addOperationWithBlockToQueue {
    // 1.創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 2.使用 addOperationWithBlock: 添加操作到隊列中
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"3---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
}

#pragma mark - NSOperation 操作依賴
//NSOperation、NSOperationQueue 最吸引人的地方是它能添加操作之間的依賴關(guān)系唉地。通過操作依賴据悔,我們可以很方便的控制操作之間的執(zhí)行先后順序。NSOperation 提供了3個接口供我們管理和查看依賴耘沼。
//- (void)addDependency:(NSOperation *)op; 添加依賴极颓,使當(dāng)前操作依賴于操作 op 的完成。
//- (void)removeDependency:(NSOperation *)op; 移除依賴群嗤,取消當(dāng)前操作對操作 op 的依賴菠隆。
//@property (readonly, copy) NSArray<NSOperation *> *dependencies; 在當(dāng)前操作開始執(zhí)行之前完成執(zhí)行的所有操作對象數(shù)組。

- (void)addDependency {
    
    // 1.創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 2.創(chuàng)建操作
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    
    // 3.添加依賴
    [op2 addDependency:op1]; // 讓op2 依賴于 op1狂秘,則先執(zhí)行op1骇径,在執(zhí)行op2
    
    // 4.添加操作到隊列中
    [queue addOperation:op1];
    [queue addOperation:op2];
}

#pragma mark - NSOperation 優(yōu)先級 & 服務(wù)質(zhì)量(只是盡可能的 并不是絕對的)
//NSOperation 提供了queuePriority(優(yōu)先級)屬性,queuePriority屬性適用于同一操作隊列中的操作者春,不適用于不同操作隊列中的操作破衔。默認情況下,所有新創(chuàng)建的操作對象優(yōu)先級都是NSOperationQueuePriorityNormal运敢。但是我們可以通過setQueuePriority:方法來改變當(dāng)前操作在同一隊列中的執(zhí)行優(yōu)先級。

//對于添加到隊列中的操作传惠,首先進入準備就緒的狀態(tài)(就緒狀態(tài)取決于操作之間的依賴關(guān)系)稻扬,然后進入就緒狀態(tài)的操作的開始執(zhí)行順序(非結(jié)束執(zhí)行順序)由操作之間相對的優(yōu)先級決定(優(yōu)先級是操作對象自身的屬性)

//準備就緒狀態(tài):當(dāng)一個操作的所有依賴都已經(jīng)完成時泰佳,操作對象通常會進入準備就緒狀態(tài),等待執(zhí)行

//queuePriority 屬性(盡可能的并不是絕對的)決定了進入準備就緒狀態(tài)下的操作之間的開始執(zhí)行順序浇坐。并且黔宛,優(yōu)先級不能取代依賴關(guān)系。
//如果一個隊列中既包含高優(yōu)先級操作觉渴,又包含低優(yōu)先級操作徽惋,并且兩個操作都已經(jīng)準備就緒,那么隊列先執(zhí)行高優(yōu)先級操作踢京。比如上例中宦棺,如果 op1 和 op4 是不同優(yōu)先級的操作渺氧,那么就會先執(zhí)行優(yōu)先級高的操作。
//如果白华,一個隊列中既包含了準備就緒狀態(tài)的操作贩耐,又包含了未準備就緒的操作,未準備就緒的操作優(yōu)先級比準備就緒的操作優(yōu)先級高管搪。那么,雖然準備就緒的操作優(yōu)先級低霎箍,也會優(yōu)先執(zhí)行澡为。優(yōu)先級不能取代依賴關(guān)系。如果要控制操作間的啟動順序顶别,則必須使用依賴關(guān)系

- (void)queuePriority{
    // 1.創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    //    設(shè)置最大并發(fā)操作數(shù) 來控制隊列的串行或者并發(fā)
//    queue.maxConcurrentOperationCount = 1; //串行
//        queue.maxConcurrentOperationCount = 8;//并發(fā)
    // 2.創(chuàng)建操作
    // 使用 NSInvocationOperation 創(chuàng)建操作1
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationTask) object:nil];
    
    // 使用 NSInvocationOperation 創(chuàng)建操作2
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationTask2) object:nil];
    
    // 使用 NSInvocationOperation 創(chuàng)建操作2
    NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationTask3) object:nil];
    
//    同一個隊列中的操作優(yōu)先級 執(zhí)行的先后 并不是結(jié)束的先后
//    op1.queuePriority = NSOperationQueuePriorityVeryHigh;
//    op2.queuePriority = NSOperationQueuePriorityHigh;
//    op3.queuePriority = NSOperationQueuePriorityNormal;
    
//    服務(wù)質(zhì)量  在iOS 8.0前,通過設(shè)置操作的優(yōu)先級,盡可能的保證某個操作優(yōu)先處理,隨著硬件性能上的提升,通過設(shè)置優(yōu)先級效果已經(jīng)越來越不明顯,在iOS 8.0后,推出了服務(wù)質(zhì)量,通過設(shè)置服務(wù)質(zhì)量,讓系統(tǒng)優(yōu)先處理某一個操作 目前也是越來越不明顯
//    op1.qualityOfService = NSQualityOfServiceUserInteractive;
//    op2.qualityOfService = NSQualityOfServiceBackground;
//    op3.qualityOfService = NSQualityOfServiceDefault;

    // 3.使用 addOperation: 添加所有操作到隊列中
    [queue addOperation:op1]; // [op1 start] 添加到隊列中 無需再自己調(diào)用start
    [queue addOperation:op2]; // [op2 start]
    [queue addOperation:op3]; // [op3 start]
}

#pragma mark - NSOperation驯绎、NSOperationQueue 線程間的通信
/**
 * 線程間通信
 */
- (void)communication {
    
    // 1.創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    // 2.添加操作
    [queue addOperationWithBlock:^{
        // 異步進行耗時操作
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
        
        // 回到主線程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            // 進行一些 UI 刷新等操作
            for (int i = 0; i < 2; i++) {
                [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
                NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
            }
        }];
    }];
}


#pragma mark - 執(zhí)行的操作
- (void)invocationTask{
    NSLog(@"1");
    for (int i = 0; i < 2; i++) {
        NSLog(@"invocationOperation1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
    }
}

- (void)invocationTask2{
    NSLog(@"2");
    for (int i = 0; i < 2; i++) {
        NSLog(@"invocationOperation2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
    }
}

- (void)invocationTask3{
    NSLog(@"3");
    for (int i = 0; i < 2; i++) {
        NSLog(@"invocationOperation3---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        [NSThread sleepForTimeInterval:2]; // 模擬耗時操作
    }
}
@end

個人博客地址:https://youyou0909.github.io

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蛤织,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌乞巧,老刑警劉巖绽媒,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件免猾,死亡現(xiàn)場離奇詭異,居然都是意外死亡获三,警方通過查閱死者的電腦和手機疙教,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門伞租,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人葵诈,你說我怎么就攤上這事±砀恚” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵广辰,是天一觀的道長。 經(jīng)常有香客問我李根,道長,這世上最難降的妖魔是什么粤攒? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任囱持,我火速辦了婚禮,結(jié)果婚禮上盔几,老公的妹妹穿的比我還像新娘掩幢。我一直安慰自己,他們只是感情好芯丧,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布缨恒。 她就那樣靜靜地躺著轮听,像睡著了一般。 火紅的嫁衣襯著肌膚如雪椒袍。 梳的紋絲不亂的頭發(fā)上藻茂,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天,我揣著相機與錄音优俘,去河邊找鬼。 笑死惭婿,一個胖子當(dāng)著我的面吹牛叶雹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钥星,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼谦炒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宁改?” 一聲冷哼從身側(cè)響起魂莫,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秽誊,沒想到半個月后琳骡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡最易,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年藻懒,在試婚紗的時候發(fā)現(xiàn)自己被綠了嬉荆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酷含。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡汪茧,死狀恐怖舱污,靈堂內(nèi)的尸體忽然破棺而出弥虐,到底是詐尸還是另有隱情,我是刑警寧澤珠插,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布颖对,位于F島的核電站丧失,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏琳拭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一坑鱼、第九天 我趴在偏房一處隱蔽的房頂上張望鲁沥。 院中可真熱鬧,春花似錦画恰、人聲如沸吸奴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽糊治。三九已至,卻和暖如春井辜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抑胎。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工阿逃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搀菩。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓破托,卻偏偏與公主長得像,于是被迫代替她去往敵國和親州既。 傳聞我的和親對象是個殘疾皇子萝映,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

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