iOS中GCD的使用小結

iOS中GCD的使用小結

作者dullgrass

2015.11.20 09:41*字數 4996閱讀 20199評論 33喜歡 192

本篇博客共分以下幾個模塊來介紹GCD的相關內容:

多線程相關概念

多線程編程技術的優缺點比較?

GCD中的三種隊列類型

The main queue(主線程串行隊列)

Global queue(全局并發隊列)

Custom queue (自定義隊列)

Group queue (隊列組)

GCD中一些系統提供的常用dispatch方法

歡迎訪問作者個人博客www.dullgrass.com,更多好的文章與您分享

多線程相關概念

進程與線程

進程概念: 進程是程序在計算機上的一次執行活動,打開一個app,就開啟了一個進程,可包含多個線程。

線程概念: 獨立執行的代碼段,一個線程同時間只能執行一個任務,反之多線程并發就可以在同一時間執行多個任務。

iOS程序中,主線程(又叫作UI線程)主要任務是處理UI事件,顯示和刷新UI,(只有主線程有直接修改UI的能力)耗時的操作放在子線程(又叫作后臺線程、異步線程)。在iOS中開子線程去處理耗時的操作,可以有效提高程序的執行效率,提高資源利用率。但是開啟線程會占用一定的內存,(主線程的堆棧大小是1M,第二個線程開始都是512KB,并且該值不能通過編譯器開關或線程API函數來更改)降低程序的性能。所以一般不要同時開很多線程。

線程相關

同步線程:同步線程會阻塞當前線程去執行線程內的任務,執行完之后才會反回當前線程。

異步線程:異步線程不會阻塞當前線程,會開啟其他線程去執行線程內的任務。

串行隊列:線程任務按先后順序逐個執行(需要等待隊列里面前面的任務執行完之后再執行新的任務)。

并發隊列:多個任務按添加順序一起開始執行(不用等待前面的任務執行完再執行新的任務),但是添加間隔往往忽略不計,所以看著像是一起執行的。

并發VS并行:并行是基于多核設備的,并行一定是并發,并發不一定是并行。

多線程中會出現的問題

Critical Section(臨界代碼段)

指的是不能同時被兩個線程訪問的代碼段,比如一個變量,被并發進程訪問后可能會改變變量值,造成數據污染(數據共享問題)。

Race Condition (競態條件)

當多個線程同時訪問共享的數據時,會發生爭用情形,第一個線程讀取改變了一個變量的值,第二個線程也讀取改變了這個變量的值,兩個線程同時操作了該變量,此時他們會發生競爭來看哪個線程會最后寫入這個變量,最后被寫入的值將會被保留下來。

Deadlock (死鎖)

兩個(多個)線程都要等待對方完成某個操作才能進行下一步,這時就會發生死鎖。

Thread Safe(線程安全)

一段線程安全的代碼(對象),可以同時被多個線程或并發的任務調度,不會產生問題,非線程安全的只能按次序被訪問。

所有Mutable對象都是非線程安全的,所有Immutable對象都是線程安全的,使用Mutable對象,一定要用同步鎖來同步訪問(@synchronized)。

互斥鎖:能夠防止多線程搶奪造成的數據安全問題,但是需要消耗大量的資源

原子屬性(atomic)加鎖

atomic: 原子屬性,為setter方法加鎖,將屬性以atomic的形式來聲明,該屬性變量就能支持互斥鎖了。

nonatomic: 非原子屬性,不會為setter方法加鎖,聲明為該屬性的變量,客戶端應盡量避免多線程爭奪同一資源。

Context Switch (上下文切換)

當一個進程中有多個線程來回切換時,context switch用來記錄執行狀態,這樣的進程和一般的多線程進程沒有太大差別,但會產生一些額外的開銷。

多線程編程技術的優缺點比較

NSThread (抽象層次:低)

優點:輕量級,簡單易用,可以直接操作線程對象

缺點: 需要自己管理線程的生命周期,線程同步。線程同步對數據的加鎖會有一定的系統開銷。

Cocoa NSOperation? (抽象層次:中)

優點:不需要關心線程管理,數據同步的事情,可以把精力放在學要執行的操作上。基于GCD,是對GCD 的封裝,比GCD更加面向對象

缺點: NSOperation是個抽象類,使用它必須使用它的子類,可以實現它或者使用它定義好的兩個子類NSInvocationOperation、NSBlockOperation.

GCD 全稱Grand Center Dispatch (抽象層次:高)

優點:是 Apple 開發的一個多核編程的解決方法,簡單易用,效率高,速度快,基于C語言,更底層更高效,并且不是Cocoa框架的一部分,自動管理線程生命周期(創建線程、調度任務、銷毀線程)。

缺點: 使用GCD的場景如果很復雜,就有非常大的可能遇到死鎖問題。

GCD抽象層次最高,使用也簡單,因此,蘋果也推薦使用GCD

GCD中的三種隊列類型

GCD編程的核心就是dispatch隊列,dispatch block的執行最終都會放進某個隊列中去進行。

The main queue(主線程串行隊列): 與主線程功能相同,提交至Main queue的任務會在主線程中執行,

Main queue 可以通過dispatch_get_main_queue()來獲取。

Global queue(全局并發隊列): 全局并發隊列由整個進程共享,有高、中(默認)、低、后臺四個優先級別。

Global queue 可以通過調用dispatch_get_global_queue函數來獲取(可以設置優先級)

Custom queue (自定義隊列): 可以為串行,也可以為并發。

Custom queue 可以通過dispatch_queue_create()來獲取;

Group queue (隊列組):將多線程進行分組,最大的好處是可獲知所有線程的完成情況。

Group queue 可以通過調用dispatch_group_create()來獲取,通過dispatch_group_notify,可以直接監聽組里所有線程完成情況。

gcd中相關函數的使用一般都是以dispatch開頭

The main queue(主線程串行隊列)

dispatch_sync 同步執行任務函數,不會開啟新的線程,dispatch_async 異步執行任務函數,會開啟新的線程

獲取主線程串行隊列

dispatch_queue_tmainQueue = dispatch_get_main_queue();

主線程串行隊列同步執行任務,在主線程運行時,會產生死鎖

dispatch_queue_tmainQueue = dispatch_get_main_queue();dispatch_sync(mainQueue,^{NSLog("MainQueue");? ? ? ? ? ? });

程序一直處于等待狀態,block中的代碼將執行不到

主線程串行隊列異步執行任務,在主線程運行,不會產生死鎖。

dispatch_queue_tmainQueue = dispatch_get_main_queue();dispatch_async(mainQueue,^{NSLog("MainQueue");? ? ? ? ? ? });

程序正常運行,block中的代碼正常運行

從子線程,異步返回主線程更新UI<這種使用方式比較多>

dispatch_queue_tglobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);dispatch_async(globalQueue, ^{//子線程異步執行下載任務,防止主線程卡頓NSURL*url = [NSURLURLWithString:@"http://www.baidu.com"];NSError*error;NSString*htmlData = [NSStringstringWithContentsOfURL:url encoding:NSUTF8StringEncodingerror:&error];if(htmlData !=nil) {dispatch_queue_tmainQueue = dispatch_get_main_queue();//異步返回主線程,根據獲取的數據,更新UIdispatch_async(mainQueue, ^{NSLog(@"根據更新UI界面");? ? ? ? });? ? }else{NSLog(@"error when download:%@",error);? ? }});

主線程串行隊列由系統默認生成的,所以無法調用dispatch_resume()和dispatch_suspend()來控制執行繼續或中斷。

Global queue(全局并發隊列)

耗時的操作,比如讀取網絡數據,IO,數據庫讀寫等,我們會在另外一個線程中處理這些操作,然后通知主線程更新界面

獲取全局并發隊列

//程序默認的隊列級別,一般不要修改,DISPATCH_QUEUE_PRIORITY_DEFAULT == 0dispatch_queue_tglobalQueue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);//HIGHdispatch_queue_tglobalQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);//LOWdispatch_queue_tglobalQueue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0);//BACKGROUNDdispatch_queue_tglobalQueue4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);

全局并發隊列同步執行任務,在主線程執行會導致頁面卡頓。

dispatch_queue_tglobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);NSLog(@"current task");dispatch_sync(globalQueue, ^{? ? sleep(2.0);NSLog(@"sleep 2.0s");});NSLog(@"next task");

控制臺輸出如下:

2015-11-1815:51:45.550Whisper[33152:345023] current task2015-11-1815:51:47.552Whisper[33152:345023] sleep2.0s2015-11-1815:51:47.552Whisper[33152:345023] next task

2s鐘之后,才會執行block代碼段下面的代碼。

全局并發隊列異步執行任務,在主線程運行,會開啟新的子線程去執行任務,頁面不會卡頓。

dispatch_queue_tglobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);NSLog(@"current task");dispatch_async(globalQueue, ^{? ? sleep(2.0);NSLog(@"sleep 2.0s");});NSLog(@"next task");

控制臺輸出如下:

2015-11-1815:50:14.999Whisper[33073:343781] current task2015-11-1815:50:15.000Whisper[33073:343781] next task2015-11-1815:50:17.004Whisper[33073:343841] sleep2.0s

主線程不用等待2s鐘,繼續執行block代碼段后面的代碼。

多個全局并發隊列,異步執行任務。

dispatch_queue_tglobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);NSLog(@"current task");dispatch_async(globalQueue, ^{NSLog(@"最先加入全局并發隊列");});dispatch_async(globalQueue, ^{NSLog(@"次加入全局并發隊列");});NSLog(@"next task");

控制臺輸出如下:

2015-11-1816:54:52.202Whisper[39827:403208] current task2015-11-1816:54:52.203Whisper[39827:403208] next task2015-11-1816:54:52.205Whisper[39827:403309] 最先加入全局并發隊列2015-11-1816:54:52.205Whisper[39827:403291] 次加入全局并發隊列

異步線程的執行順序是不確定的。幾乎同步開始執行

全局并發隊列由系統默認生成的,所以無法調用dispatch_resume()和dispatch_suspend()來控制執行繼續或中斷。

Custom queue (自定義隊列)

自定義串行隊列

獲取自定義串行隊列

dispatch_queue_tserialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);NSLog(@"%s",dispatch_queue_get_label(conCurrentQueue)) ;

控制臺輸出:

2015-11-1911:05:34.469Whisper[1223:42960] com.dullgrass.serialQueue

dispatch_queue_create(const char *label, dispatch_queue_attr_t

attr)函數中第一個參數是給這個queue起的標識,這個在調試的可以看到是哪個隊列在執行,或者在crash日志中,也能做為提示。第二個是需要創建的隊列類型,是串行的還是并發的

自定義串行隊列同步執行任務

dispatch_queue_tserialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);NSLog(@"current task");dispatch_sync(serialQueue, ^{NSLog(@"最先加入自定義串行隊列");sleep(2);});dispatch_sync(serialQueue, ^{NSLog(@"次加入自定義串行隊列");});NSLog(@"next task");

控制臺輸出:

2015-11-1817:09:40.025Whisper[40241:416296] current task2015-11-1817:09:40.027Whisper[40241:416296] 最先加入自定義串行隊列2015-11-1817:09:43.027Whisper[40241:416296] 次加入自定義串行隊列2015-11-1817:09:43.027Whisper[40241:416296] next task

當前線程等待串行隊列中的子線程執行完成之后再執行,串行隊列中先進來的子線程先執行任務,執行完成后,再執行隊列中后面的任務。

自定義串行隊列嵌套執行同步任務,產生死鎖

dispatch_queue_tserialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);dispatch_sync(serialQueue, ^{//該代碼段后面的代碼都不會執行,程序被鎖定在這里NSLog(@"會執行的代碼");dispatch_sync(serialQueue, ^{NSLog(@"代碼不執行");});});

異步執行串行隊列,嵌套同步執行串行隊列,同步執行的串行隊列中的任務將不會被執行,其他程序正常執行

dispatch_queue_tserialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);dispatch_async(serialQueue, ^{NSLog(@"會執行的代碼");dispatch_sync(serialQueue, ^{NSLog(@"代碼不執行");});});

注意不要嵌套使用同步執行的串行隊列任務

自定義并發隊列

獲取自定義并發隊列

dispatch_queue_tconCurrentQueue =? dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);

自定義并發隊列執行同步任務

dispatch_queue_tconCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);NSLog(@"current task");dispatch_sync(conCurrentQueue, ^{NSLog(@"先加入隊列");});dispatch_sync(conCurrentQueue, ^{NSLog(@"次加入隊列");});NSLog(@"next task");

控制臺輸出如下:

2015-11-1910:36:23.259Whisper[827:20596] current task2015-11-1910:36:23.261Whisper[827:20596] 先加入隊列2015-11-1910:36:23.261Whisper[827:20596] 次加入隊列2015-11-1910:36:23.261Whisper[827:20596] next task

自定義并發隊列嵌套執行同步任務(不會產生死鎖,程序正常運行)

dispatch_queue_tconCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);NSLog(@"current task");dispatch_sync(conCurrentQueue, ^{NSLog(@"先加入隊列");dispatch_sync(conCurrentQueue, ^{NSLog(@"次加入隊列");? ? });});NSLog(@"next task");

控制臺輸出如下:

2015-11-1910:39:21.301Whisper[898:22273] current task2015-11-1910:39:21.303Whisper[898:22273] 先加入隊列2015-11-1910:39:21.303Whisper[898:22273] 次加入隊列2015-11-1910:39:21.303Whisper[898:22273] next task

自定義并發隊列執行異步任務

dispatch_queue_tconCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);NSLog(@"current task");dispatch_async(conCurrentQueue, ^{NSLog(@"先加入隊列");});dispatch_async(conCurrentQueue, ^{NSLog(@"次加入隊列");});NSLog(@"next task");

控制臺輸出如下:

2015-11-1910:45:22.290Whisper[1050:26445] current task2015-11-1910:45:22.290Whisper[1050:26445] next task2015-11-1910:45:22.290Whisper[1050:26505] 次加入隊列2015-11-1910:45:22.290Whisper[1050:26500] 先加入隊列

異步執行任務,開啟新的子線程,不影響當前線程任務的執行,并發隊列中的任務,幾乎是同步執行的,輸出順序不確定

Group queue (隊列組)

當遇到需要執行多個線程并發執行,然后等多個線程都結束之后,再匯總執行結果時可以用group queue

使用場景: 同時下載多個圖片,所有圖片下載完成之后去更新UI(需要回到主線程)或者去處理其他任務(可以是其他線程隊列)。

原理:使用函數dispatch_group_create創建dispatch

group,然后使用函數dispatch_group_async來將要執行的block任務提交到一個dispatch

queue。同時將他們添加到一個組,等要執行的block任務全部執行完成之后,使用dispatch_group_notify函數接收完成時的消息。

使用示例:

dispatch_queue_tconCurrentGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);dispatch_queue_tmainQueue = dispatch_get_main_queue();dispatch_group_t groupQueue = dispatch_group_create();NSLog(@"current task");dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{NSLog(@"并行任務1");});dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{NSLog(@"并行任務2");});dispatch_group_notify(groupQueue, mainQueue, ^{NSLog(@"groupQueue中的任務 都執行完成,回到主線程更新UI");});NSLog(@"next task");

控制臺輸出:

2015-11-1913:47:55.117Whisper[1645:97116] current task2015-11-1913:47:55.117Whisper[1645:97116] next task2015-11-1913:47:55.119Whisper[1645:97178] 并行任務12015-11-1913:47:55.119Whisper[1645:97227] 并行任務22015-11-1913:47:55.171Whisper[1645:97116] groupQueue中的任務 都執行完成,回到主線程更新UI

在當前線程阻塞的同步等待dispatch_group_wait

dispatch_group_t groupQueue = dispatch_group_create();dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW,10*NSEC_PER_SEC);dispatch_queue_tconCurrentGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);NSLog(@"current task");dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{longisExecuteOver = dispatch_group_wait(groupQueue, delayTime);if(isExecuteOver) {NSLog(@"wait over");? }else{NSLog(@"not over");? }NSLog(@"并行任務1");});dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{NSLog(@"并行任務2");});

控制臺輸出如下:

2015-11-1914:37:29.514Whisper[2426:126683] current task2015-11-1914:37:29.518Whisper[2426:126791] 并行任務22015-11-1914:37:39.515Whisper[2426:126733] wait over2015-11-1914:37:39.516Whisper[2426:126733] 并行任務1

dispatch_time(dispatch_time_t when, int64_t delta);

參數注釋:

第一個參數一般是DISPATCH_TIME_NOW,表示從現在開始

第二個參數是延時的具體時間

延時1秒可以寫成如下幾種:

NSEC_PER_SEC----每秒有多少納秒

dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC);

USEC_PER_SEC----每秒有多少毫秒(注意是指在納秒的基礎上)

dispatch_time(DISPATCH_TIME_NOW, 1000*USEC_PER_SEC); //SEC---毫秒

NSEC_PER_USEC----每毫秒有多少納秒。

dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC*NSEC_PER_USEC);SEC---納秒

GCD中一些系統提供的常用dispatch方法

dispatch_after延時添加到隊列

使用示例:

dispatch_time_t delayTime3 = dispatch_time(DISPATCH_TIME_NOW,3*NSEC_PER_SEC);dispatch_time_t delayTime2 = dispatch_time(DISPATCH_TIME_NOW,2*NSEC_PER_SEC);dispatch_queue_tmainQueue = dispatch_get_main_queue();NSLog(@"current task");dispatch_after(delayTime3, mainQueue, ^{NSLog(@"3秒之后添加到隊列");});dispatch_after(delayTime2, mainQueue, ^{NSLog(@"2秒之后添加到隊列");});NSLog(@"next task");

控制臺輸出如下:

2015-11-1915:50:19.369Whisper[2725:172593] current task2015-11-1915:50:19.370Whisper[2725:172593] next task2015-11-1915:50:21.369Whisper[2725:172593]2秒之后添加到隊列2015-11-1915:50:22.654Whisper[2725:172593]3秒之后添加到隊列

dispatch_after只是延時提交block,并不是延時后立即執行,并不能做到精確控制,需要精確控制的朋友慎用哦

dispatch_apply在給定的隊列上多次執行某一任務,在主線程直接調用會阻塞主線程去執行block中的任務。

dispatch_apply函數的功能:把一項任務提交到隊列中多次執行,隊列可以是串行也可以是并行,dispatch_apply不會立刻返回,在執行完block中的任務后才會返回,是同步執行的函數。

dispatch_apply正確使用方法:為了不阻塞主線程,一般把dispatch_apply放在異步隊列中調用,然后執行完成后通知主線程

使用示例:

dispatch_queue_tglobalQueue = dispatch_get_global_queue(0,0);NSLog(@"current task");dispatch_async(globalQueue, ^{dispatch_queue_tapplyQueue = dispatch_get_global_queue(0,0);//第一個參數,3--block執行的次數//第二個參數,applyQueue--block任務提交到的隊列//第三個參數,block--需要重復執行的任務dispatch_apply(3, applyQueue, ^(size_t index) {NSLog(@"current index %@",@(index));? ? ? sleep(1);});NSLog(@"dispatch_apply 執行完成");dispatch_queue_tmainQueue = dispatch_get_main_queue();dispatch_async(mainQueue, ^{NSLog(@"回到主線程更新UI");});});NSLog(@"next task");

控制臺輸出如下:

2015-11-1916:24:45.015Whisper[4034:202269] current task2015-11-1916:24:45.016Whisper[4034:202269] next task2015-11-1916:24:45.016Whisper[4034:202347] current index02015-11-1916:24:45.016Whisper[4034:202344] current index12015-11-1916:24:45.016Whisper[4034:202345] current index22015-11-1916:24:46.021Whisper[4034:202347] dispatch_apply 執行完成2015-11-1916:24:46.021Whisper[4034:202269] 回到主線程更新UI

嵌套使用dispatch_apply會導致死鎖。

dispatch_once保證在app運行期間,block中的代碼只執行一次

經典使用場景---單例

單例對象ShareManager的定義:

ShareManager的.h文件#import@interfaceShareManager:NSObject@property(nonatomic,copy)NSString*someProperty;+ (ShareManager *)shareManager;+ (ShareManager *)sharedManager;@endShareManager的.m文件#import"ShareManager.h"@implementationShareManagerstaticShareManager *sharedManager =nil;//GCD實現單例功能+ (ShareManager *)shareManager{staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{? ? sharedManager = [[selfalloc] init]; });returnsharedManager;}//在ARC下,非GCD,實現單例功能+ (ShareManager *)sharedManager{@synchronized(self) {if(!sharedManager) {? ? ? ? sharedManager = [[selfalloc] init];? ? } }returnsharedManager;}- (instancetype)init{self= [superinit];if(self) {? ? ? _someProperty =@"Default Property Value"; }returnself;}@endShareManager的使用#import"ShareManager.h"在需要使用的函數中,直接調用下面的方法ShareManager *share = [ShareManager sharedManager];NSLog(@"share is %@",share.someProperty);

dispatch_barrier_async 柵欄的作用

功能:是在并行隊列中,等待在dispatch_barrier_async之前加入的隊列全部執行完成之后(這些任務是并發執行的)再執行dispatch_barrier_async中的任務,dispatch_barrier_async中的任務執行完成之后,再去執行在dispatch_barrier_async之后加入到隊列中的任務(這些任務是并發執行的)。

使用示例:

dispatch_queue_tconCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(conCurrentQueue, ^{NSLog(@"dispatch 1");});dispatch_async(conCurrentQueue, ^{NSLog(@"dispatch 2");});dispatch_barrier_async(conCurrentQueue, ^{NSLog(@"dispatch barrier");});dispatch_async(conCurrentQueue, ^{NSLog(@"dispatch 3");});dispatch_async(conCurrentQueue, ^{NSLog(@"dispatch 4");});

控制臺輸出如下:

2015-11-1918:12:34.125Whisper[22633:297257] dispatch12015-11-1918:12:34.125Whisper[22633:297258] dispatch22015-11-1918:12:34.126Whisper[22633:297258] dispatch barrier2015-11-1918:12:34.127Whisper[22633:297258] dispatch32015-11-1918:12:34.127Whisper[22633:297257] dispatch4

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,327評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,996評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,316評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,406評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,128評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,524評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,576評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,759評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,310評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,065評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,249評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,821評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,479評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,909評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,140評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,984評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,228評論 2 375

推薦閱讀更多精彩內容

  • 本篇博客共分以下幾個模塊來介紹GCD的相關內容: 多線程相關概念 多線程編程技術的優缺點比較? GCD中的三種隊列...
    dullgrass閱讀 37,874評論 30 236
  • 本篇博客共分以下幾個模塊來介紹GCD的相關內容: 多線程相關概念 多線程編程技術的優缺點比較? GCD中的三種隊列...
    有夢想的老伯伯閱讀 1,029評論 0 4
  • 背景 擔心了兩周的我終于輪到去醫院做胃鏡檢查了!去的時候我都想好了最壞的可能(胃癌),之前在網上查的癥狀都很相似。...
    Dely閱讀 9,262評論 21 42
  • GCD筆記 總結一下多線程部分,最強大的無疑是GCD,那么先從這一塊部分講起. Dispatch Queue的種類...
    jins_1990閱讀 780評論 0 1
  • 今天是暑假的倒數第二天了,后天就要開學了,我很激動,因為我馬上就要見到我思念的老師和同學們了! ...
    Greybear_閱讀 416評論 0 1