MG--多線(xiàn)程總結(jié)(詳細(xì))

  • 什么是進(jìn)程?

    • 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序

    • 每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專(zhuān)用且受保護(hù)的內(nèi)存空間內(nèi)

    • 比如同時(shí)打開(kāi)酷狗责球、Xcode焦履,系統(tǒng)就會(huì)分別啟動(dòng)2個(gè)進(jìn)程


      啟動(dòng)2個(gè)進(jìn)程
  • 什么是線(xiàn)程?

    • 1個(gè)進(jìn)程要想執(zhí)行任務(wù),必須得有線(xiàn)程(每1個(gè)進(jìn)程至少要有1條線(xiàn)程)
    • 一個(gè)進(jìn)程(程序)的所有任務(wù)都在線(xiàn)程中執(zhí)行
  • 什么是多線(xiàn)程?

    • 1個(gè)進(jìn)程中可以開(kāi)啟多條線(xiàn)程雏逾,多條線(xiàn)程可以并行(同時(shí))執(zhí)行不同的任務(wù)
    • 多線(xiàn)程技術(shù)可以提高程序的執(zhí)行效率
    • eg: 進(jìn)程-->車(chē)間嘉裤,線(xiàn)程 -->車(chē)間工人(--> 是 “相當(dāng)于” “比作”的意思)
  • 多線(xiàn)程的優(yōu)點(diǎn)
    1.能適當(dāng)提高程序的執(zhí)行效率
    2.能適當(dāng)提高資源利用率(CPU、內(nèi)存利用率)

  • 多線(xiàn)程的缺點(diǎn)
    1.創(chuàng)建線(xiàn)程是有開(kāi)銷(xiāo)的栖博,iOS下主要成本包括:內(nèi)核數(shù)據(jù)結(jié)構(gòu)(大約1KB)屑宠、棧空間(子線(xiàn)程512KB仇让、主線(xiàn)程1MB典奉,也可以使用-setStackSize:設(shè)置躺翻,但必須是4K的倍數(shù),而且最小是16K)秋柄,創(chuàng)建線(xiàn)程大約需要90毫秒的創(chuàng)建時(shí)間
    2.如果開(kāi)啟大量的線(xiàn)程获枝,會(huì)降低程序的性能
    3.線(xiàn)程越多,CPU在調(diào)度線(xiàn)程上的開(kāi)銷(xiāo)就越大
    4.程序設(shè)計(jì)更加復(fù)雜:比如線(xiàn)程之間的通信骇笔、多線(xiàn)程的數(shù)據(jù)共享



多線(xiàn)程在iOS開(kāi)發(fā)中的應(yīng)用

  • 什么是主線(xiàn)程?

  • 一個(gè)iOS程序運(yùn)行后,默認(rèn)會(huì)開(kāi)啟1條線(xiàn)程嚣崭,稱(chēng)為“主線(xiàn)程”或“UI線(xiàn)程”

  • 主線(xiàn)程

    • 顯示/刷新UI界面
    • 處理UI事件(比如點(diǎn)擊事件笨触、滾動(dòng)事件、拖拽事件等)
  • 主線(xiàn)程的使用注意

    • 別將比較耗時(shí)的操作放到主線(xiàn)程中
    • 耗時(shí)操作會(huì)卡住主線(xiàn)程雹舀,嚴(yán)重影響UI的流暢度芦劣,給用戶(hù)一種“卡”的壞體驗(yàn)


      耗時(shí)操作會(huì)卡住主線(xiàn)程



iOS中多線(xiàn)程的實(shí)現(xiàn)方案

| 技術(shù)方案 | 簡(jiǎn)介 | 語(yǔ)言 | 線(xiàn)程生命周期 | 使用頻率 |
| -------- | -----: | :----: | | :----: | | :----: |
| 1.pthread | 1.一套通用的多線(xiàn)程API。 2.適用于Unix\Linux\Windows等系統(tǒng)说榆。 3.跨平臺(tái)\可移植虚吟。 4.使用難度大 | C | 程序員管理 | 幾乎不用 |
| 2.NSThread | 1.使用更加面向?qū)ο?。 2.簡(jiǎn)單易用签财,可直接操作線(xiàn)程對(duì)象 | OC | 程序員管理 | 偶爾使用 |
| 3.GCD |1.旨在替代NSThread等線(xiàn)程技術(shù)串慰。2.充分利用設(shè)備的多核 | C | 自動(dòng)管理 | 經(jīng)常使用 |
| 4.NSOperation | 1.基于GCD(底層是GCD)。2.比GCD多了一些更簡(jiǎn)單實(shí)用的功能唱蒸。3.使用更加面向?qū)ο? | OC | 自動(dòng)管理 | 經(jīng)常使用 |


iOS中多線(xiàn)程的實(shí)現(xiàn)方案

1.pthread(了解)

  • 創(chuàng)建pthread
    • pthread_create
  • 只要create一次就會(huì)創(chuàng)建一個(gè)新的線(xiàn)程
  • 系統(tǒng)會(huì)自動(dòng)在子線(xiàn)程中調(diào)用傳入的函數(shù)
/*
第一個(gè)參數(shù): 線(xiàn)程的代號(hào)(當(dāng)做就是線(xiàn)程)
第二個(gè)參數(shù): 線(xiàn)程的屬性
第三個(gè)參數(shù): 指向函數(shù)的指針, 就是將來(lái)線(xiàn)程需要執(zhí)行的方法
第四個(gè)參數(shù): 給第三個(gè)參數(shù)的指向函數(shù)的指針 傳遞的參數(shù)
void *(*functionP)(void *)
void *  == id
一般情況下C語(yǔ)言中的類(lèi)型都是以 _t或者Ref結(jié)尾
*/
pthread_t threadId;
// 只要create一次就會(huì)創(chuàng)建一個(gè)新的線(xiàn)程
pthread_create(&threadId , NULL, &demo, "lym");

2. NSThread(了解/掌握)

  • 一個(gè)NSThread對(duì)象就代表一條線(xiàn)程
  • 幾種創(chuàng)建方式
  • 第一種
    • alloc + init
    • 注意: 需要手動(dòng)啟動(dòng)線(xiàn)程
    • 特點(diǎn): 系統(tǒng)內(nèi)部會(huì)retain當(dāng)前線(xiàn)程
    • 只有線(xiàn)程中的方法執(zhí)行完畢, 系統(tǒng)才會(huì)將其釋放
// 第一種創(chuàng)建方式
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];
// 線(xiàn)程一啟動(dòng)邦鲫,就會(huì)在線(xiàn)程thread中執(zhí)行self的run方法
  • 第二種
    • 不用手動(dòng)調(diào)用start方法, 系統(tǒng)會(huì)自動(dòng)啟動(dòng)
    • 沒(méi)有返回值, 不能對(duì)線(xiàn)程進(jìn)行更多的設(shè)置
    • 應(yīng)用場(chǎng)景: 需要快速簡(jiǎn)便的執(zhí)行線(xiàn)程
// 第二種創(chuàng)建方式
// 創(chuàng)建線(xiàn)程后自動(dòng)啟動(dòng)線(xiàn)程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
// 系統(tǒng)就會(huì)自動(dòng)創(chuàng)建一個(gè)子線(xiàn)程, 并且在子線(xiàn)程中自動(dòng)執(zhí)行self的@selector方法(隱式創(chuàng)建并啟動(dòng)線(xiàn)程)
[self performSelectorInBackground:@selector(run) withObject:nil];
/**
上述2種創(chuàng)建線(xiàn)程方式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):簡(jiǎn)單快捷
缺點(diǎn):無(wú)法對(duì)線(xiàn)程進(jìn)行更詳細(xì)的設(shè)置
*/
  • 控制線(xiàn)程狀態(tài)
    • 創(chuàng)建出來(lái) -> 新建狀態(tài)
    • 調(diào)用start -> 準(zhǔn)備就緒
    • 被CPU調(diào)用 -> 運(yùn)行
    • sleep -> 阻塞
    • 執(zhí)行完畢, 或者被強(qiáng)制關(guān)閉 -> 死亡
      • 注意: 如果強(qiáng)制關(guān)閉線(xiàn)程, 關(guān)閉之后的其它操作都無(wú)法執(zhí)行


        線(xiàn)程狀態(tài)
// 啟動(dòng)線(xiàn)程
//進(jìn)入就緒狀態(tài) -> 運(yùn)行狀態(tài)。當(dāng)線(xiàn)程任務(wù)執(zhí)行完畢神汹,自動(dòng)進(jìn)入死亡狀態(tài)
-(void)start;
阻塞(暫停)線(xiàn)程
//進(jìn)入阻塞狀態(tài)
+(void)sleepUntilDate:(NSDate *)date;
+(void)sleepForTimeInterval:(NSTimeInterval)time;
強(qiáng)制停止線(xiàn)程
//進(jìn)入死亡狀態(tài)
+(void)exit;
###warm 注意:一旦線(xiàn)程停止(死亡)了庆捺,就不能再次開(kāi)啟任務(wù)```

- 主線(xiàn)程相關(guān)用法

+(NSThread*)mainThread; // 獲得主線(xiàn)程
-(BOOL)isMainThread; // 是否為主線(xiàn)程
+(BOOL)isMainThread; // 是否為主線(xiàn)程
NSThread *current = [NSThread currentThread]; // 獲得當(dāng)前線(xiàn)程
-(void)setName:(NSString *)name;// setter線(xiàn)程的名字
-(NSString *)name; // getter線(xiàn)程的名字```

  • 多線(xiàn)程的安全隱患

    • 資源共享
      • 塊資源可能會(huì)被多個(gè)線(xiàn)程共享,也就是多個(gè)線(xiàn)程可能會(huì)訪(fǎng)問(wèn)同一塊資源
      • 比如多個(gè)線(xiàn)程訪(fǎng)問(wèn)同一個(gè)對(duì)象屁魏、同一個(gè)變量滔以、同一個(gè)文件
      • 當(dāng)多個(gè)線(xiàn)程訪(fǎng)問(wèn)同一塊資源時(shí),很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問(wèn)題


        存取錢(qián)

        賣(mài)票買(mǎi)票
  • 安全隱患解決– 互斥鎖

    • 互斥鎖使用格式
@synchronized(鎖對(duì)象)
{ // 需要鎖定的代碼  }
注意:鎖定1份代碼只用1把鎖氓拼,用多把鎖是無(wú)效的```
  - 互斥鎖的優(yōu)缺點(diǎn)
    >- 優(yōu)點(diǎn):能有效防止因多線(xiàn)程搶奪資源造成的數(shù)據(jù)安全問(wèn)題
    - 缺點(diǎn):需要消耗大量的CPU資源
  - 互斥鎖的使用前提:多條線(xiàn)程搶奪同一塊資源
  - 相關(guān)專(zhuān)業(yè)術(shù)語(yǔ):線(xiàn)程同步
    >- 線(xiàn)程同步的意思是:多條線(xiàn)程在同一條線(xiàn)上執(zhí)行(按順序地執(zhí)行任務(wù))
    - 互斥鎖你画,就是使用了線(xiàn)程同步技術(shù)

- ####NSThread線(xiàn)程間通信
  - 什么叫做線(xiàn)程間通信
    - 在1個(gè)進(jìn)程中,線(xiàn)程往往不是孤立存在的披诗,多個(gè)線(xiàn)程之間需要經(jīng)常進(jìn)行通信
  - 線(xiàn)程間通信的體現(xiàn)
    - 1個(gè)線(xiàn)程傳遞數(shù)據(jù)給另1個(gè)線(xiàn)程
    - 在1個(gè)線(xiàn)程中執(zhí)行完特定任務(wù)后撬即,轉(zhuǎn)到另1個(gè)線(xiàn)程繼續(xù)執(zhí)行任務(wù)

  - 線(xiàn)程間通信常用方法

-(void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
-(void)performSelector:(SEL)aSelector onThread:(NSThread *)thread withObject:(id)arg waitUntilDone:(BOOL)wait;```


圖片下載
performSelectorOnMainThread方法中waitUntilDone:NO參數(shù)的含義
如果傳入的是YES: 那么會(huì)等到主線(xiàn)程中的方法執(zhí)行完畢, 才會(huì)繼續(xù)執(zhí)行下面其他行的代碼
如果傳入的是NO: 那么不用等到主線(xiàn)程中的方法執(zhí)行完畢, 就可以繼續(xù)執(zhí)行下面其他行的代碼
注意點(diǎn): 更新UI一定要在主線(xiàn)程中更新
[self performSelectorInBackground:@selector(download2:) withObject:url];

[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:NO];

[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];

[self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];

擴(kuò)展
  • 原子和非原子屬性

    • atomic:原子屬性,為setter方法加鎖(默認(rèn)就是atomic)
    • nonatomic:非原子屬性呈队,不會(huì)為setter方法加鎖
    • 注意點(diǎn): atomic系統(tǒng)自動(dòng)給我們添加的鎖不是互斥鎖/ 自旋鎖
  • 自旋鎖和互斥鎖對(duì)比

    • 共同點(diǎn)
      • 都能夠保證多線(xiàn)程在同一時(shí)候, 只能有一個(gè)線(xiàn)程操作鎖定的代碼
    • 不同點(diǎn)
      • 如果是互斥鎖, 假如現(xiàn)在被鎖住了, 那么后面來(lái)得線(xiàn)程就會(huì)進(jìn)入”休眠”狀態(tài), 直到解鎖之后, 又會(huì)喚醒線(xiàn)程繼續(xù)執(zhí)行
      • 如果是自旋鎖, 假如現(xiàn)在被鎖住了, 那么后面來(lái)得線(xiàn)程不會(huì)進(jìn)入休眠狀態(tài), 會(huì)一直傻傻的等待, 直到解鎖之后立刻執(zhí)行
      • 自旋鎖更適合做一些較短的操作



3.GCD(掌握/理解)

什么是GCD

全稱(chēng)是Grand Central Dispatch剥槐,可譯為“牛逼的中樞調(diào)度器”
純C語(yǔ)言,提供了非常多強(qiáng)大的函數(shù)

  • GCD的優(yōu)勢(shì)
  • GCD是蘋(píng)果公司為多核的并行運(yùn)算提出的解決方案
  • GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核宪摧、四核)

  • GCD會(huì)自動(dòng)管理線(xiàn)程的生命周期(創(chuàng)建線(xiàn)程粒竖、調(diào)度任務(wù)颅崩、銷(xiāo)毀線(xiàn)程)

  • 程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫(xiě)任何線(xiàn)程管理代碼

  • GCD中有2個(gè)核心概念

  • 任務(wù):執(zhí)行什么操作
  • 隊(duì)列:用來(lái)存放任務(wù)

  • GCD的使用就2個(gè)步驟

    • 定制任務(wù)
    • 確定想做的事情
    • 將任務(wù)添加到隊(duì)列中
      • GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出蕊苗,放到對(duì)應(yīng)的線(xiàn)程中執(zhí)行
      • 任務(wù)的取出遵循隊(duì)列的FIFO原則:先進(jìn)先出沿后,后進(jìn)后出
  • 如何執(zhí)行任務(wù)

    • 同步函數(shù)dispatch_sync
    • 不具備開(kāi)啟新線(xiàn)程的能力
    • 異步函數(shù)dispatch_async
      • 具備開(kāi)啟新線(xiàn)程的能力
    • 同步和異步主要影響:能不能開(kāi)啟新的線(xiàn)程


  • 隊(duì)列的類(lèi)型

    • 并發(fā)隊(duì)列(Concurrent Dispatch Queue)
    • 可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開(kāi)啟多個(gè)線(xiàn)程同時(shí)執(zhí)行任務(wù))
    • 并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
    • 自己創(chuàng)建: dispatch_queue_t queue = dispatch_queue_create("com.Mg.lym", DISPATCH_QUEUE_CONCURRENT);
    • 全局并發(fā)隊(duì)列 : dispatch_get_global_queue(0 , 0);
###使用dispatch_queue_create函數(shù)創(chuàng)建隊(duì)列
dispatch_queue_t
dispatch_queue_create(const char *label, //  隊(duì)列名稱(chēng) 
dispatch_queue_attr_t attr); // 隊(duì)列的類(lèi)型
dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_CONCURRENT);

###GCD默認(rèn)已經(jīng)提供了全局的并發(fā)隊(duì)列,供整個(gè)應(yīng)用使用朽砰,可以無(wú)需手動(dòng)創(chuàng)建
使用dispatch_get_global_queue函數(shù)獲得全局的并發(fā)隊(duì)列
dispatch_queue_t
dispatch_get_global_queue(
dispatch_queue_priority_t priority, // 隊(duì)列的優(yōu)先級(jí) 
unsigned long flags); // 此參數(shù)暫時(shí)無(wú)用尖滚,用0即可

###獲得全局并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

###全局并發(fā)隊(duì)列的優(yōu)先級(jí)
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默認(rèn)(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后臺(tái)```
***

   >- 串行隊(duì)列(Serial Dispatch Queue)
    - 讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行(一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù))

GCD中獲得串行有2種途徑

使用dispatch_queue_create函數(shù)創(chuàng)建串行隊(duì)列
// 創(chuàng)建串行隊(duì)列(隊(duì)列類(lèi)型傳遞NULL或者DISPATCH_QUEUE_SERIAL)

dispatch_queue_t queue = dispatch_queue_create("com.520it.queue",NULL);

使用主隊(duì)列(跟主線(xiàn)程相關(guān)聯(lián)的隊(duì)列)

主隊(duì)列是GCD自帶的一種特殊的串行隊(duì)列
放在主隊(duì)列中的任務(wù)瞧柔,都會(huì)放到主線(xiàn)程中執(zhí)行
使用dispatch_get_main_queue()獲得主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();```



  • 有4個(gè)術(shù)語(yǔ)比較容易混淆:同步漆弄、異步、并發(fā)造锅、串行

    • 同步和異步主要影響:能不能開(kāi)啟新的線(xiàn)程
    • 同步:只是在當(dāng)前線(xiàn)程中執(zhí)行任務(wù)撼唾,不具備開(kāi)啟新線(xiàn)程的能力
    • 異步:可以在新的線(xiàn)程中執(zhí)行任務(wù),具備開(kāi)啟新線(xiàn)程的能力
    • 并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式
      • 并發(fā):允許多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行
      • 串行:一個(gè)任務(wù)執(zhí)行完畢后哥蔚,再執(zhí)行下一個(gè)任務(wù)
  • GCD的各種組合

  • 異步 + 并行 = 會(huì)開(kāi)啟新的線(xiàn)程
- 異步函數(shù), 會(huì)先執(zhí)行完所有的代碼, 再在子線(xiàn)程中執(zhí)行任務(wù)
  • 異步 + 串行 = 會(huì)創(chuàng)建新的線(xiàn)程, 但是只會(huì)創(chuàng)建一個(gè)新的線(xiàn)程, 所有的任務(wù)都在這一個(gè)新的線(xiàn)程中執(zhí)行
  • 同步 + 并行 = 不會(huì)開(kāi)啟新的線(xiàn)程
    • 其實(shí)就相當(dāng)于同步 + 串行
    • 同步函數(shù), 只要代碼執(zhí)行到了同步函數(shù)的那一行, 就會(huì)立即執(zhí)行任務(wù), 只有任務(wù)執(zhí)行完畢才會(huì)繼續(xù)往后執(zhí)行
  • 同步 + 串行 = 不會(huì)創(chuàng)建新的線(xiàn)程
  • 異步 + 主隊(duì)列 = 不會(huì)開(kāi)啟新的線(xiàn)程
    • 只要是主隊(duì)列, 永遠(yuǎn)都在主線(xiàn)程中執(zhí)行
  • 同步 + 主隊(duì)列 = 需要記住的就一點(diǎn): 同步函數(shù)不能搭配主隊(duì)列使用
    • 注意: 有例外的情況, 如果同步函數(shù)是在異步函數(shù)中調(diào)用的, 那么沒(méi)有任何問(wèn)題
GCD的各種組合
  • GCD線(xiàn)程間通信示例
    • 利用異步函數(shù)執(zhí)行任務(wù)
    • 利用主隊(duì)列回到主線(xiàn)程更新UI
// 從子線(xiàn)程回到主線(xiàn)程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
        //執(zhí)行耗時(shí)的異步操作...
        dispatch_async(dispatch_get_main_queue(),^{
           //回到主線(xiàn)程倒谷,執(zhí)行UI刷新操作
        });
});
  • GCD中的常用方法
    • 延遲執(zhí)行
    ### 使用NSTimer
   [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];

    // 內(nèi)部實(shí)現(xiàn)原理就是NSTimer
    ### 調(diào)用NSObject的方法
   [self performSelector:@selector(run) withObject:nil afterDelay:2.0];

    // 將需要執(zhí)行的代碼, 和方法放在一起, 提高代碼的閱讀性
    // 相比NSTimer來(lái)說(shuō), GCD的延遲執(zhí)行更加準(zhǔn)確
    ### 使用GCD函數(shù)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"%@", [NSThread currentThread]);
        NSLog(@"run");
};
### run方法
-(void)run
{
        NSLog(@"%s", __func__);
}
  • 一次性代碼
    • 整個(gè)程序運(yùn)行過(guò)程中, 只會(huì)執(zhí)行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
        NSLog(@"明哥寫(xiě)的代碼被執(zhí)行了");
});

  • 快速迭代
/*
     第一個(gè)參數(shù): 需要執(zhí)行幾次任務(wù)
     第二個(gè)參數(shù): 隊(duì)列
     第三個(gè)參數(shù): 當(dāng)前被執(zhí)行到得任務(wù)的索引
 /*
     dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
       NSLog(@"%@, %zd",[NSThread currentThread] , index);
     });
### 通過(guò)比較for循環(huán)和apply遍歷時(shí)間,看看哪個(gè)比較高效
- (void)apply
{
    /*
     第一個(gè)參數(shù): 需要執(zhí)行幾次任務(wù)
     第二個(gè)參數(shù): 隊(duì)列
     第三個(gè)參數(shù): 當(dāng)前被執(zhí)行到得任務(wù)的索引
     */
    /*
     dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
     NSLog(@"%@, %zd",[NSThread currentThread] , index);
     });
     */
    
    // 1.獲取images文件夾中所有的文件
    NSString *sourcePath = @"/Users/ming/Desktop/abc";
    NSString *dest = @"/Users/ming/Desktop/lym";
    
    // 2.獲取images文件夾中所有的文件
    NSFileManager *mgr = [NSFileManager defaultManager];
    NSArray *subPaths = [mgr subpathsAtPath:sourcePath];
    //    NSLog(@"%@", subPaths);

    // 3.剪切文件到lym文件夾中
    /*
     CFAbsoluteTime begin = CFAbsoluteTimeGetCurrent();
     for (int i = 0; i < subPaths.count; i++) {
     // 3.1獲取當(dāng)前遍歷到得文件的名稱(chēng)
     NSString *fileNmae = subPaths[i];
     // 3.2根據(jù)當(dāng)前文件的名稱(chēng), 拼接全路徑
     NSString *fromPath = [sourcePath stringByAppendingPathComponent:fileNmae];
     NSString *toPath = [dest stringByAppendingPathComponent:fileNmae];
     NSLog(@"fromPath = %@", fromPath);
     NSLog(@"toPath = %@", toPath);
     
     [mgr moveItemAtPath:fromPath toPath:toPath error:nil];
     }
     CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
     NSLog(@"花費(fèi)了%f秒", end -begin);
     */
    
    
    CFAbsoluteTime begin = CFAbsoluteTimeGetCurrent();
    dispatch_apply(subPaths.count, dispatch_get_global_queue(0, 0), ^(size_t index) {
        // 3.1獲取當(dāng)前遍歷到得文件的名稱(chēng)
        NSString *fileNmae = subPaths[index];
        // 3.2根據(jù)當(dāng)前文件的名稱(chēng), 拼接全路徑
        NSString *fromPath = [sourcePath stringByAppendingPathComponent:fileNmae];
        NSString *toPath = [dest stringByAppendingPathComponent:fileNmae];
        NSLog(@"fromPath = %@", fromPath);
        NSLog(@"toPath = %@", toPath);
        
        [mgr moveItemAtPath:fromPath toPath:toPath error:nil];
        
    });
    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
    NSLog(@"花費(fèi)了%f秒", end -begin); // 花費(fèi)了0.001280秒
}

  • barrier
  • 要想執(zhí)行完前面所有的任務(wù)再執(zhí)行barrier必須滿(mǎn)足兩個(gè)條件
  • 所有任務(wù)都是在同一個(gè)隊(duì)列中

  • 隊(duì)列不能是全局并行隊(duì)列, 必須是自己創(chuàng)建的隊(duì)列

  • barrier方法之前添加的任務(wù)會(huì)先被執(zhí)行, 只有等barrier方法之前添加的任務(wù)執(zhí)行完畢, 才會(huì)執(zhí)行barrier

  • 而且如果是在barrier方法之后添加的任務(wù), 必須等barrier方法執(zhí)行完畢之后才會(huì)開(kāi)始執(zhí)行

  • 隊(duì)列組(group)

    • 如果想實(shí)現(xiàn), 等前面所有的任務(wù)都執(zhí)行完畢, 再執(zhí)行某一個(gè)特定的任務(wù), 那么可以通過(guò)GCD中的組來(lái)實(shí)現(xiàn)
    • 只要當(dāng)前組中所有的任務(wù)都執(zhí)行完畢了, 那么系統(tǒng)會(huì)自動(dòng)調(diào)用dispatch_group_notify

實(shí)際開(kāi)發(fā): 有這么1種需求

  • 首先:分別異步執(zhí)行2個(gè)耗時(shí)的操作
  • 其次:等2個(gè)異步操作都執(zhí)行完畢后糙箍,再回到主線(xiàn)程執(zhí)行操作
  • 如果想要快速高效地實(shí)現(xiàn)上述需求渤愁,可以考慮用隊(duì)列組/柵欄函數(shù)
  • 詳細(xì)實(shí)現(xiàn)
使用group實(shí)現(xiàn)
- (void)group
{
    // GCD組
    // 1.創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_CONCURRENT);
    // 創(chuàng)建一個(gè)組
    dispatch_group_t group = dispatch_group_create();
    
    // 2.添加一個(gè)下載圖片任務(wù)
    dispatch_group_async(group, queue, ^{
        NSURL *url = [NSURL URLWithString:@"http://stimgcn1.s-msn.com/msnportal/ent/2015/08/04/7a59dbe7-3c18-4fae-bb56-305dab5e6951.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        self.image1 = [UIImage imageWithData:data];
        NSLog(@"下載圖片1 %@", [NSThread currentThread]);
    });
    
    // 3.再添加一個(gè)下載圖片任務(wù)
    dispatch_group_async(group, queue, ^{
        NSURL *url = [NSURL URLWithString:@"http://y1.ifengimg.com/cmpp/2015/08/05/04/15495f65-5cd2-44cd-a704-bc455d629fe3_size25_w510_h339.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        self.image2 = [UIImage imageWithData:data];
        NSLog(@"下載圖片2 %@", [NSThread currentThread]);
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"合成圖片 %@", [NSThread currentThread]);
        // 1.創(chuàng)建圖片上下文
        UIGraphicsBeginImageContext(CGSizeMake(200, 200));
        // 2.繪制第一張圖片
        [self.image1 drawInRect:CGRectMake(0, 0, 100, 200)];
        // 3.繪制第二張圖片
        [self.image2 drawInRect:CGRectMake(100, 0, 100, 200)];
        // 4.從上下文中取出圖片
        UIImage *res = UIGraphicsGetImageFromCurrentImageContext();
        // 5.關(guān)閉上下文
        UIGraphicsEndImageContext();
        
        // 6.回到主線(xiàn)程更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"更新UI %@", [NSThread currentThread]);
            
            self.imageView.image = res;
        });
    });
}
柵欄函數(shù)實(shí)現(xiàn)
- (void)barrier
{
     // 如果所有的任務(wù)都在"同一個(gè)"隊(duì)列中
     // 那么在barrier方法之前添加的任務(wù)會(huì)先被執(zhí)行, 只有等barrier方法之前添加的任務(wù)執(zhí)行完畢, 才會(huì)執(zhí)行barrier
     // 而且如果是在barrier方法之后添加的任務(wù), 必須等barrier方法執(zhí)行完畢之后才會(huì)開(kāi)始執(zhí)行
    // 1.創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_CONCURRENT);
//    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 2.添加一個(gè)下載圖片任務(wù)
    dispatch_async(queue, ^{
        
        NSURL *url = [NSURL URLWithString:@"http://stimgcn1.s-msn.com/msnportal/ent/2015/08/04/7a59dbe7-3c18-4fae-bb56-305dab5e6951.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        self.image1 = [UIImage imageWithData:data];
        NSLog(@"下載圖片1 %@", [NSThread currentThread]);
    });
    // 3.再添加一個(gè)下載圖片任務(wù)
    dispatch_async(queue, ^{
        
        NSURL *url = [NSURL URLWithString:@"http://y1.ifengimg.com/cmpp/2015/08/05/04/15495f65-5cd2-44cd-a704-bc455d629fe3_size25_w510_h339.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        self.image2 = [UIImage imageWithData:data];
        NSLog(@"下載圖片2 %@", [NSThread currentThread]);
    });
    

    
    // 要想執(zhí)行完前面所有的任務(wù)再執(zhí)行barrier必須滿(mǎn)足兩個(gè)條件
    // 1. 所有任務(wù)都是在同一個(gè)隊(duì)列中
    // 2. 隊(duì)列不能是全局并行隊(duì)列, 必須是自己創(chuàng)建的隊(duì)列
    dispatch_barrier_async(queue, ^{
        NSLog(@"合成圖片 %@", [NSThread currentThread]);
        // 1.創(chuàng)建圖片上下文
        UIGraphicsBeginImageContext(CGSizeMake(200, 200));
        // 2.繪制第一張圖片
        [self.image1 drawInRect:CGRectMake(0, 0, 100, 200)];
        // 3.繪制第二張圖片
        [self.image2 drawInRect:CGRectMake(100, 0, 100, 200)];
        // 4.從上下文中取出圖片
        UIImage *res = UIGraphicsGetImageFromCurrentImageContext();
        // 5.關(guān)閉上下文
        UIGraphicsEndImageContext();
        
        // 6.回到主線(xiàn)程更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"更新UI %@", [NSThread currentThread]);
            
            self.imageView.image = res;
        });
    });
}



4.NSOperation(掌握/理解)

  • NSOperation的作用
    • 配合使用NSOperation和NSOperationQueue也能實(shí)現(xiàn)多線(xiàn)程編程
  • NSOperation和NSOperationQueue實(shí)現(xiàn)多線(xiàn)程的具體步驟:
    • 1.先將需要執(zhí)行的操作封裝到一個(gè)NSOperation對(duì)象中
    • 2.然后將NSOperation對(duì)象添加到NSOperationQueue中
    • 3.系統(tǒng)會(huì)自動(dòng)將NSOperationQueue中的NSOperation取出來(lái)
    • 4.將取出的NSOperation封裝的操作放到一條新線(xiàn)程中執(zhí)行

NSOperation的子類(lèi)

  • NSOperation是個(gè)抽象類(lèi),并不具備封裝操作的能力倍靡,必須使用它的子類(lèi)

  • 使用NSOperation子類(lèi)的方式有3種

    • NSInvocationOperation
    • NSBlockOperation
    • 自定義子類(lèi)繼承NSOperation猴伶,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法

NSInvocationOperation

  • 創(chuàng)建NSInvocationOperation對(duì)象
    - (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;

  • 調(diào)用start方法開(kāi)始執(zhí)行操作
    - (void)start;

    • 一旦執(zhí)行操作,就會(huì)調(diào)用target的sel方法
  • 注意

    • 默認(rèn)情況下塌西,調(diào)用了start方法后并不會(huì)開(kāi)一條新線(xiàn)程去執(zhí)行操作他挎,而是在當(dāng)前線(xiàn)程同步執(zhí)行操作
    • 只有將NSOperation放到一個(gè)NSOperationQueue中,才會(huì)異步執(zhí)行操作

NSBlockOperation

  • 創(chuàng)建NSBlockOperation對(duì)象
    + (id)blockOperationWithBlock:(void (^)(void))block;

  • 通過(guò)addExecutionBlock:方法添加更多的操作
    - (void)addExecutionBlock:(void (^)(void))block;

  • 注意:只要NSBlockOperation封裝的操作數(shù) >1捡需,就會(huì)異步執(zhí)行操作

NSOperationQueue

  • NSOperationQueue的作用

    • NSOperation可以調(diào)用start方法來(lái)執(zhí)行任務(wù)办桨,但默認(rèn)是同步執(zhí)行的
    • 如果將NSOperation添加到NSOperationQueue(操作隊(duì)列)中,系統(tǒng)會(huì)自動(dòng)異步執(zhí)行NSOperation中的操作
  • 添加操作到NSOperationQueue中

-(void)addOperation:(NSOperation *)op;
-(void)addOperationWithBlock:(void (^)(void))block;
  • 什么是并發(fā)數(shù)

    • 同時(shí)執(zhí)行的任務(wù)數(shù)
    • 比如站辉,同時(shí)開(kāi)3個(gè)線(xiàn)程執(zhí)行3個(gè)任務(wù)呢撞,并發(fā)數(shù)就是3
  • 最大并發(fā)數(shù)的相關(guān)方法

-(NSInteger)maxConcurrentOperationCount;
-(void)setMaxConcurrentOperationCount:(NSInteger)cnt;
  • 取消隊(duì)列的所有操作
-(void)cancelAllOperations;
提示:也可以調(diào)用NSOperation的- (void)cancel方法取消單個(gè)操作
  • 暫停和恢復(fù)隊(duì)列
-(void)setSuspended:(BOOL)b;// YES代表暫停隊(duì)列,NO代表恢復(fù)隊(duì)列
-(BOOL)isSuspended;
  • 操作依賴(lài)

    • NSOperation之間可以設(shè)置依賴(lài)來(lái)保證執(zhí)行順序

      • 比如一定要讓操作A執(zhí)行完后饰剥,才能執(zhí)行操作B殊霞,可以這么寫(xiě)[operationB addDependency:operationA]; // 操作B依賴(lài)于操作A
    • 可以在不同queue的NSOperation之間創(chuàng)建依賴(lài)關(guān)系


      操作依賴(lài)
  • 操作的監(jiān)聽(tīng)

    • 可以監(jiān)聽(tīng)一個(gè)操作的執(zhí)行完畢
-(void (^)(void))completionBlock;
-(void)setCompletionBlock: (void (^)(void))block;
  • 自定義NSOperation
    • 自定義NSOperation的步驟很簡(jiǎn)單

      • 重寫(xiě)- (void)main方法,在里面實(shí)現(xiàn)想執(zhí)行的任務(wù)
    • 重寫(xiě)-(void)main方法的注意點(diǎn)

      • 自己創(chuàng)建自動(dòng)釋放池(因?yàn)槿绻钱惒讲僮魈兀瑹o(wú)法訪(fǎng)問(wèn)主線(xiàn)程的自動(dòng)釋放池)
      • 經(jīng)常通過(guò)- (BOOL)isCancelled方法檢測(cè)操作是否被取消绷蹲,對(duì)取消做出響應(yīng)

NSOperation基本使用

//
//  ViewController.m
//  了解-NSOperation基本使用
//
//  Created by ming on 16/6/6.
//  Copyright (c) 2015年 ming. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"%s", __func__);
    //1. 封裝任務(wù)
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        // 主線(xiàn)程
        NSLog(@"1---%@", [NSThread currentThread]);
    }];
    
    // 2.追加其它任務(wù)
    // 注意: 在沒(méi)有隊(duì)列的情況下, 如果給BlockOperation追加其它任務(wù), 那么其它任務(wù)會(huì)在子線(xiàn)程中執(zhí)行
    [op1 addExecutionBlock:^{
        NSLog(@"2---%@", [NSThread currentThread]);
    }];
    [op1 addExecutionBlock:^{
        NSLog(@"3---%@", [NSThread currentThread]);
    }];
    
    // 3.啟動(dòng)任務(wù)
    [op1 start];
}


- (void)invocation
{
    // 注意: 父類(lèi)不具備封裝操作的能力
    //    NSOperation *op = [[NSOperation alloc] init];
    
    // 1.封裝任務(wù)
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
    // 2.要想執(zhí)行任務(wù)必須調(diào)用start
    [op1 start];
    
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run2) object:nil];
    [op2 start];
}
- (void)run
{
    NSLog(@"%@", [NSThread currentThread]);
}
- (void)run2
{
    NSLog(@"%@", [NSThread currentThread]);
}

@end

NSOpreatinoQueue的其它常用方法

//
//  ViewController.m
//  NSOpreatinoQueue的其它常用方法
//
//  Created by apple on 16/6/6.
//  Copyright (c) 2016年 Ming. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    
    // 1.創(chuàng)建隊(duì)列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];

    // 2.創(chuàng)建任務(wù)
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1-------%@", [NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2-------%@", [NSThread currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"3-------%@", [NSThread currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"4-------%@", [NSThread currentThread]);
        for (int i = 0; i < 1000; i++) {
            NSLog(@"%i", i);
        }
    }];
    NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"5-------%@", [NSThread currentThread]);
    }];
    
    
    // 3.添加依賴(lài)
    [op5 addDependency:op1];
    [op5 addDependency:op2];
    [op5 addDependency:op3];
    [op5 addDependency:op4];

    
    // 4.監(jiān)聽(tīng)op4什么時(shí)候執(zhí)行完畢
    op4.completionBlock = ^{
        NSLog(@"op4中所有的操作都執(zhí)行完畢了");
    };
    
    // 4.添加任務(wù)到隊(duì)列
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue2 addOperation:op3];
    [queue2 addOperation:op4];
    [queue addOperation:op5];
    
    
    /*
//    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_queue_t queue = dispatch_queue_create("com.ming.lym", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue2 = dispatch_queue_create("com.ming.lbj", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"1-------%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-------%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-------%@", [NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue2, ^{
        NSLog(@"4-------%@", [NSThread currentThread]);
    });
     */
    /*
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_t group2 = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
         NSLog(@"1-------%@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"2-------%@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"3-------%@", [NSThread currentThread]);
    });
    dispatch_group_notify(group2, queue, ^{
        NSLog(@"4-------%@", [NSThread currentThread]);
    });
    */
    
}
@end


  • 自定義NSOperation下載圖片思路– 無(wú)沙盒緩存


    NSOperation下載-無(wú)沙盒緩存
  • 自定義NSOperation下載圖片思路– 有沙盒緩存

NSOperation下載-有沙盒緩存
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子祝钢,更是在濱河造成了極大的恐慌比规,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拦英,死亡現(xiàn)場(chǎng)離奇詭異蜒什,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)疤估,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)灾常,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人做裙,你說(shuō)我怎么就攤上這事岗憋。” “怎么了锚贱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)关串。 經(jīng)常有香客問(wèn)我拧廊,道長(zhǎng),這世上最難降的妖魔是什么晋修? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任吧碾,我火速辦了婚禮,結(jié)果婚禮上墓卦,老公的妹妹穿的比我還像新娘倦春。我一直安慰自己,他們只是感情好落剪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布睁本。 她就那樣靜靜地躺著,像睡著了一般忠怖。 火紅的嫁衣襯著肌膚如雪呢堰。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天凡泣,我揣著相機(jī)與錄音枉疼,去河邊找鬼。 笑死鞋拟,一個(gè)胖子當(dāng)著我的面吹牛骂维,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贺纲,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼航闺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了哮笆?” 一聲冷哼從身側(cè)響起来颤,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤汰扭,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后福铅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體萝毛,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年滑黔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了笆包。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡略荡,死狀恐怖庵佣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情汛兜,我是刑警寧澤巴粪,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站粥谬,受9級(jí)特大地震影響肛根,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜漏策,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一派哲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧掺喻,春花似錦芭届、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至抑月,卻和暖如春树叽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谦絮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工题诵, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人层皱。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓性锭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親叫胖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子草冈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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