iOS多線程開(kāi)發(fā)之GCD

多線程概念

  • 線程
    線程指的是:1個(gè)CPU執(zhí)行的CPU命令列為一條無(wú)分叉路徑
  • 多線程
    這種無(wú)分叉路徑不止一條,存在多條即為"多線程".在多線程中,1個(gè)CPU核執(zhí)行多條不同路徑上的不同命令.需要明確的是:不管CPU技術(shù)如何,基本上1個(gè)CPU核一次能夠執(zhí)行的CPU命令始終為1.使用多線程的程序可以在某一個(gè)線程和其他線程之間反復(fù)進(jìn)行上下文切換,因此,看上去好像1個(gè)CPU能夠并列的執(zhí)行多個(gè)線程一樣,而在具有多個(gè)CPU核的情況下,就能夠提供多個(gè)CPU核并行執(zhí)行多個(gè)線程的技術(shù).
  1. 多線程的缺點(diǎn)
    多線程技術(shù)看起來(lái)非常美好,但實(shí)際上因?yàn)樯婕暗缴舷挛那袚Q,多線程執(zhí)行的效率未必比單線程快,甚至可能會(huì)慢過(guò)單線程,而且,多個(gè)線程更新相同資源會(huì)導(dǎo)致數(shù)據(jù)的不一致(數(shù)據(jù)競(jìng)爭(zhēng)),停止等待事件的線程會(huì)導(dǎo)致多個(gè)線程相互持續(xù)等待(死鎖),使用太多線程會(huì)消耗掉大量?jī)?nèi)存等問(wèn)題.
  2. 多線程的優(yōu)點(diǎn)
    盡管極易發(fā)生各種問(wèn)題,在iOS中也應(yīng)當(dāng)使用多線程編程.因?yàn)槎嗑€程編程可以保證應(yīng)用程序的響應(yīng)性能.
    應(yīng)用程序在啟動(dòng)時(shí),通過(guò)最先執(zhí)行額線程("主線程")來(lái)描繪用戶界面,處理屏幕的事件.如果在該線程中進(jìn)行長(zhǎng)時(shí)間的處理,如數(shù)據(jù)庫(kù)訪問(wèn),網(wǎng)絡(luò)請(qǐng)求等,就會(huì)妨礙主線程的執(zhí)行,從而導(dǎo)致不能更新用戶界面,應(yīng)用程序的畫(huà)面長(zhǎng)時(shí)間停滯的問(wèn)題.
    3.為什么要使用多線程
    使用多線程編程,在執(zhí)行長(zhǎng)時(shí)間的處理時(shí),仍可保證用戶界面的響應(yīng)性能.這是我們使用多線程編程的最大好處,而且,蘋(píng)果為了簡(jiǎn)化多線程的使用,給我們提供了多種多線程技術(shù),本文主要介紹GCD結(jié)合NSoperation在開(kāi)發(fā)中的使用.

GCD的API

  1. Dispatch Queue
    Dispatch Queue 是執(zhí)行處理的等待隊(duì)列.,應(yīng)用程序編程人員通過(guò)dispatch_async等API,在block中將要執(zhí)行的處理追加到Dispatch Queue 中,Dispatch Queue 按照追加的順序(FIFO,先進(jìn)先出)執(zhí)行處理.
通過(guò)Dispatch Queue 執(zhí)行處理.png

Dispatch Queue 分為兩種類型

  • Serial Dispatch Queue 等待現(xiàn)在執(zhí)行中處理結(jié)束
  • Concurrent Dispatch Queue 不等待現(xiàn)在執(zhí)行中處理結(jié)束
    <pre>
    -(void)serialDispatchQueue{
    //serial Dispatch queue 的創(chuàng)建
    dispatch_queue_t queue = dispatch_queue_create("DC.test01", NULL);
    for (int i = 0; i< 10; i++) {
    dispatch_async(queue, ^{
    NSLog(@"%d\n",i);
    });
    }
    }
  • (void)concurrentDispatchQueue{
    //Concurrent Dispatch queue 的創(chuàng)建
    dispatch_queue_t queue = dispatch_queue_create("DC.test01", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i< 10; i++) {
    dispatch_async(queue, ^{
    NSLog(@"%d\n",i);
    });
    }
    }
    </pre>

上面的代碼中,分別創(chuàng)建了serial Dispatch Queue 和Concurrent Dispatch Queue.當(dāng)為serial Dispatch Queu 時(shí),因?yàn)橐却F(xiàn)在執(zhí)行的處理結(jié)束,所以首先執(zhí)行第一個(gè)任務(wù),打印0,然后順序依次執(zhí)行其他任務(wù)(這里表現(xiàn)為0,1,2,3,4,5,6,7,8,9由小到大按順序打印).系統(tǒng)只會(huì)使用一個(gè)線程.當(dāng)為Concurrent Dispatch Queu 時(shí),因?yàn)椴挥玫却F(xiàn)在執(zhí)行的處理結(jié)束,所以首先執(zhí)行第一個(gè)任務(wù),不管第一個(gè)任務(wù)執(zhí)行是否結(jié)束,都開(kāi)始執(zhí)行第二個(gè)任務(wù),不管第二個(gè)任務(wù)執(zhí)行是否結(jié)束,都開(kāi)始執(zhí)行第三個(gè)任務(wù),如此重復(fù)循環(huán).(這里表現(xiàn)為0,1,2,3,4,5,6,7,8,9的打印沒(méi)有按照有小到大的順序,是一個(gè)隨機(jī)順序).系統(tǒng)可以并行執(zhí)行多個(gè)處理,但是并行執(zhí)行處理數(shù)量取決于當(dāng)前系統(tǒng)的狀態(tài),即iOS基于Dispatch Queue 中的處理數(shù),CPU核數(shù)以及CPU負(fù)荷等當(dāng)前系統(tǒng)狀態(tài)來(lái)決定Concurrent Dispatch Queue中并行執(zhí)行的處理數(shù).

Dispatch Queue 隊(duì)列種類

當(dāng)生成多個(gè)serial Dispatch Queue,各個(gè)serial Dispatch Queue 將并行執(zhí)行,雖然在一個(gè)Serial Dispatch queue 中同時(shí)只能后執(zhí)行一個(gè)追加處理,但是如果將處理分別追加到4個(gè)serial Dispatch queue 中,各個(gè)serial Dispatch queue 執(zhí)行1個(gè),即為同時(shí)執(zhí)行4個(gè)處理.如果生成2000個(gè)serial Dispatch queue ,那么久生成2000個(gè)線程,而不像Concurrent Dispatch queue 那樣,系統(tǒng)會(huì)根據(jù)系統(tǒng)狀態(tài)來(lái)決定執(zhí)行處理數(shù)(生成線程的個(gè)數(shù)).如果過(guò)多使用線程,就會(huì)消耗大量?jī)?nèi)存,引起大量的上下文切換,大幅降低系統(tǒng)的響應(yīng)性能.因此,只在為了避免多線程變成問(wèn)題之一---多個(gè)線程更新相同資源導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)時(shí)使用serial Dispatch queue.

  • 除了使用了dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)這個(gè)方法去創(chuàng)建Dispatch Queue .實(shí)際上,不用特意生成Dispatch Queue ,我們也可以獲取系統(tǒng)提供標(biāo)準(zhǔn)的Dispatch queue.那就是 Main Dispatch Queue 和Global Dispatch queue .
  • Main Dispatch Queue 實(shí)在主線程中執(zhí)行的Dispatch queue ,因?yàn)橹骶€程只有一個(gè),所以他自然是serial Dispatch queue ,追加到main Dispatch queue 的處理在主線程的RunLoop中執(zhí)行.
  • Global Dispatch queue 是所有的應(yīng)用程序都能夠使用的Concurrent Dispatch Queue .沒(méi)有必要通過(guò)Dispatch_queue_creat 函數(shù)來(lái)生成.另外,Global Dispatch queue 有四個(gè)執(zhí)行優(yōu)先級(jí),分別是高優(yōu)先級(jí)(High Priority),默認(rèn)優(yōu)先級(jí)(Default Priority),低優(yōu)先級(jí)(Low Priority)和后臺(tái)優(yōu)先級(jí)(Background Priority).
    <pre>
    //獲取系統(tǒng)提供標(biāo)準(zhǔn)的Dispatch queue.
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_queue_t globalHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_queue_t globalDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t globalLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    dispatch_queue_t globalbackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    </pre>

Dispatch_queue_create 函數(shù)生成的Dispatch queue 不管是serial 還是Concurrent,都使用與默認(rèn)優(yōu)先級(jí)Global Dispatch Queue 相同有限優(yōu)先級(jí)的線程,可以使用Dispatch_set_target_queue函數(shù)變更Dispatch queue 的優(yōu)先級(jí).

2.Dispatch Group
在追加到Dispatch Queue 中的多個(gè)處理全部結(jié)束后想執(zhí)行結(jié)束處理,開(kāi)發(fā)中經(jīng)常會(huì)碰到這種需求.當(dāng)只是用一個(gè)serial Dispatch queue 的時(shí)候,只要將想執(zhí)行的結(jié)果全部追加到serial Dispatch queue 中并在最后追加結(jié)束處理即可.但是在使用Concurrent Dispatch queue 或同時(shí)使用多個(gè)Dispatch queue 是.Dispatch Group就派上用場(chǎng)了.
<pre>
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"complete");
});

dispatch_group_async(group, queue, ^{
    NSLog(@"blko");
});
dispatch_group_async(group, queue, ^{
    NSLog(@"blk1");
});
dispatch_group_async(group, queue, ^{
    NSLog(@"blk2");
});
dispatch_group_async(group, queue, ^{
    NSLog(@"blk3");
});

//打印結(jié)果為 blk2 blk1 blko blk3
2016-04-23 15:00:00.260 test01[1687:171508] complete
</pre>

上面這段代碼展示了Dispatch Group的用法.Dispatch Group 可以監(jiān)視追到到Dispatch queue 中的處理的完成情況,一旦監(jiān)測(cè)到所有處理執(zhí)行結(jié)束,就將結(jié)束的處理追加到 dispatch_group_notify中指定的Dispatch queue 中執(zhí)行.

除了使用dispatch_group_async 追加處理到Dispatch queue中,還有另外一函數(shù):Dispatch_group_enter() 和Dispatch_group_leave().

2016.7.11更新:使用Dispatch_group_enter() 和Dispatch_group_leave()可以對(duì)網(wǎng)絡(luò)請(qǐng)求等異步執(zhí)行線程也執(zhí)行回調(diào)監(jiān)聽(tīng)

Paste_Image.png

3.dispatch_barrier_async
在訪問(wèn)數(shù)據(jù)庫(kù)或文件時(shí),使用serial Dispatch queue 可以避免數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題.寫(xiě)入處理確實(shí)不可與其他的寫(xiě)入處理以及包含讀取的其他某些處理并行執(zhí)行,但是如果讀取處理只是與讀取處理并行執(zhí)行,那么多個(gè)并行執(zhí)行處理就不會(huì)發(fā)生問(wèn)題.也就是說(shuō),為了高效率的訪問(wèn),讀取處理追加到Concurrent Dispatch queue 中,寫(xiě)入處理在任一個(gè)讀取處理都沒(méi)有執(zhí)行的狀態(tài)下,追加到serial Dispatch queue中即可.用之前的幾個(gè)接口也可以實(shí)現(xiàn)這個(gè)功能,但是蘋(píng)果系統(tǒng)了一個(gè)非常方便解決這個(gè)問(wèn)題的接口:dispatch_barrier_async.用代碼來(lái)演示dispatch_barrier_async的使用.

<pre>
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(queue, ^{ NSLog(@"reading1"); });
dispatch_async(queue, ^{ NSLog(@"reading2"); });
dispatch_async(queue, ^{ NSLog(@"reading3"); });
dispatch_async(queue, ^{ NSLog(@"reading4"); });

dispatch_barrier_async(queue, ^{ NSLog(@"writing1"); });

dispatch_async(queue, ^{ NSLog(@"reading5"); });
dispatch_async(queue, ^{ NSLog(@"reading6"); });
dispatch_async(queue, ^{ NSLog(@"reading7"); });
dispatch_async(queue, ^{ NSLog(@"reading8"); });

</pre>

上面的代碼打印結(jié)果為:2016-04-23 15:21:20.474 test01[1782:184574] reading1
2016-04-23 15:21:20.474 test01[1782:184575] reading2 reading3 reading4
2016-04-23 15:21:20.475 test01[1782:184635] writing1
2016-04-23 15:21:20.475 test01[1782:184574] reading5 reading6 reading7 reading8

Dispatch_barrier_async 函數(shù)會(huì)等待追加到Concurrent Dispatch Queue 上的并行執(zhí)行的處理全部結(jié)束之后,再將指定的處理追加到該Concurrent Dispatch Queue 中,然后等待由Dispatch_barrier_async 追加的處理結(jié)束后,Concurrent Dispatch Queue才恢復(fù)為一般的動(dòng)作.用下圖來(lái)表示更加明了.將Concurrent Dispatch Queue分為三段.使用Concurrent Dispatch Queue 和Dispatch_barrier_async可以實(shí)現(xiàn)高效的函數(shù)庫(kù)訪問(wèn)和文件訪問(wèn).

Dispatch_barrier_async函數(shù)處理流程

4.dispatch_sync
Dispatch_async 函數(shù)的async意味著非同步,就是將指定的Block非同步的追加到指定的Dispatch queue中,Dispatch_async函數(shù)不做任何等待.
Dispatch_sync 函數(shù)的sync意味著同步,就是將指定的Block同步的追加到指定的Dispatch queue中,在追加的Block結(jié)束前,Dispatch_sync函數(shù)會(huì)一直等待.

等待意味著當(dāng)前線程停止,開(kāi)發(fā)中一定要非常注意這種情況(容易引起死鎖).Dispatch_sync其實(shí)可以看做簡(jiǎn)易的Dispatch_group_wait函數(shù).一旦調(diào)用Dispatch_sync函數(shù),那么在指定的處理執(zhí)行結(jié)束之前,該函數(shù)不會(huì)返回.

<pre>
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"Hello 1");
dispatch_sync(queue, ^{
NSLog(@"Hello 2");
});
});

dispatch_sync(queue, ^{
    NSLog(@"Hello 1");
});

</pre>

分析以上代碼,main Dispatch Queue 中執(zhí)行的Block 等待 main Dispatch Queue中要執(zhí)行的Block
執(zhí)行結(jié)束.引起死鎖.

5.Dispatch Semaphore
如前所述,當(dāng)并行執(zhí)行的處理更新數(shù)據(jù)時(shí),會(huì)產(chǎn)生數(shù)據(jù)不一致的情況,有時(shí)程序還會(huì)異常結(jié)束.雖然使用serial Dispatch queue和Dispatch_barrier_async 函數(shù)可以避免這類問(wèn)題,但是當(dāng)需要進(jìn)行更細(xì)粒度的排他控制時(shí).我們就需要用到Dispatch semaphone了.
Dispatch semaphore 是持有計(jì)數(shù)的信號(hào),該計(jì)數(shù)是多線程編程中的計(jì)數(shù)類型信號(hào).在Dispatch Semaphore中.使用計(jì)數(shù)類實(shí)現(xiàn)該功能,計(jì)數(shù)為0時(shí)等待,計(jì)數(shù)為1或者大于1時(shí),減去1而不等待.比較兩段代碼:

  • 代碼一:
    <pre>
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    NSMutableArray *array = [[NSMutableArray alloc] init];
    for (int i = 0; i < 100000; i++) {
    dispatch_async(queue, ^{
    [array addObject:[NSNumber numberWithInt:i]];
    });
    }
    運(yùn)行后報(bào)錯(cuò):test01(2034,0x10c403000) malloc: *** error for object 0x7fc1e263cbb8: incorrect checksum for freed object - object was probably modified after being freed.
    *** set a breakpoint in malloc_error_break to debug
    (lldb)
    </pre>

該代碼使用global Dispatch queue 更新NSMutableArray,所以執(zhí)行后,有內(nèi)存錯(cuò)誤導(dǎo)致程序異常結(jié)束的概率很高.

  • 代碼二:
    <pre>
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //生成Dispatch Semaphore ,計(jì)數(shù)初始值設(shè)定為1,保證可訪問(wèn)NSMutableArray類對(duì)象的線程同時(shí)只能有1個(gè)
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

    NSMutableArray *array = [[NSMutableArray alloc] init];

    for (int i = 0; i < 100000; i++) {
    dispatch_async(queue, ^{

          //等待Dispatch Semaphore ,一直等待.直到Dispatch Semaphore的計(jì)數(shù)值達(dá)到或者大于1
          dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
          
          //由于Dispatch Semaphore的計(jì)數(shù)值大于或等于1,Dispatch Semaphore的計(jì)數(shù)值減1,dispatch_semaphore_wait執(zhí)行返回.
          //此時(shí)Dispatch Semaphore的計(jì)數(shù)值等于0.由于可訪問(wèn)NSMutableArray類對(duì)象的線程同時(shí)只能有1個(gè),因此可安全的進(jìn)行更新
          [array addObject:[NSNumber numberWithInt:i]];
          
          //排他控制處理結(jié)束,Dispatch Semaphore的計(jì)數(shù)值加1
          dispatch_semaphore_signal(semaphore);
      });
    

    }
    NSLog(@"%lu",(unsigned long)array.count);
    打印結(jié)果test01[2045:222660] 99999,更新成功
    </pre>

也可以參考同步塊(synchronization block) 和NSLock的使用.

5.Dispatch_after
有時(shí)候會(huì)有這種情況,想在指定的時(shí)間后執(zhí)行處理.這時(shí)候可以考慮使用Dispatch_after.需要注意的是,Dispatch_after并不是在指定的時(shí)間后執(zhí)行處理,而只是在指定的時(shí)間追加處理到Dispatch queue.因?yàn)镸ian Dispatch queue在主線程的RunLoop中執(zhí)行,所以在比如每隔1/60秒執(zhí)行的RunLoop中,Block最快3秒后執(zhí)行,最慢在3+1/60后執(zhí)行.雖然在有嚴(yán)格時(shí)間的要求下使用Dispatch_after會(huì)出問(wèn)題,但在想大致延遲執(zhí)行處理時(shí)可以使用.

6.Dispatch_once
使用Dispatch_once來(lái)執(zhí)行只需要運(yùn)行一次的線程安全代碼,即單例模式.常用的寫(xiě)法如下:
<pre>

  • (instancetype)shareInstance{

    static DCtest *shareInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    shareInstance = [[self alloc] init];
    });
    return shareInstance;
    }
    </pre>

NSOperationQueue

GCD技術(shù)確實(shí)非常棒,然而還有一種技術(shù)"NSOperationQueue",在某些情況下,使用NSOperationQueue比GCD更方便,我們也應(yīng)該熟悉了解.

  • 取消某個(gè)操作
    在GCD中只負(fù)責(zé)往隊(duì)列中添加任務(wù),無(wú)法取消.然而如果使用操作隊(duì)列.運(yùn)行任務(wù)之前, 可以在NSOperation對(duì)象上調(diào)用cancel方法,該方法會(huì)設(shè)置對(duì)象內(nèi)的標(biāo)志位,用以表示此任務(wù)不需執(zhí)行,不過(guò),已啟動(dòng)的任務(wù)無(wú)法取消.
  • 指定操作間的依賴關(guān)系
    一個(gè)操作可以依賴其他多個(gè)操作.開(kāi)發(fā)者能指定操作之間的依賴體系,是指定的操作必須在另外一個(gè)操 作順利執(zhí)行完畢后方可執(zhí)行.
  • 指定操作的優(yōu)先級(jí)
    GCD也有優(yōu)先級(jí),不過(guò)只能指定隊(duì)列的優(yōu)先級(jí),而不能指定某個(gè)操作的優(yōu)先級(jí).
  • 通知鍵值觀測(cè)機(jī)制監(jiān)控NSOperation對(duì)象的屬性
    NSOperation對(duì)象有許多屬性都適合通過(guò)鍵值觀測(cè)機(jī)制(KVO)來(lái)監(jiān)聽(tīng),.比如可以通過(guò)isCancelled屬性來(lái)判斷任務(wù)是否已取消,也可以通過(guò)isFinished來(lái)判斷任務(wù)是否已完成.如果想在某個(gè)任務(wù)變更起狀態(tài)是得到通知,那么鍵值觀測(cè)很有用.

在多線程開(kāi)發(fā)中,我們可以結(jié)合GCD和NSOperation,來(lái)更高效的實(shí)現(xiàn)多線程編程.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末沐祷,一起剝皮案震驚了整個(gè)濱河市澡刹,隨后出現(xiàn)的幾起案子领舰,更是在濱河造成了極大的恐慌庐舟,老刑警劉巖矮台,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異茄蚯,居然都是意外死亡镜硕,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)双谆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)壳咕,“玉大人,你說(shuō)我怎么就攤上這事顽馋∥嚼澹” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵趣避,是天一觀的道長(zhǎng)庞呕。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么住练? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任地啰,我火速辦了婚禮,結(jié)果婚禮上讲逛,老公的妹妹穿的比我還像新娘亏吝。我一直安慰自己,他們只是感情好盏混,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布蔚鸥。 她就那樣靜靜地躺著,像睡著了一般许赃。 火紅的嫁衣襯著肌膚如雪止喷。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天混聊,我揣著相機(jī)與錄音弹谁,去河邊找鬼。 笑死句喜,一個(gè)胖子當(dāng)著我的面吹牛预愤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播咳胃,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼植康,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了展懈?” 一聲冷哼從身側(cè)響起销睁,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎标沪,沒(méi)想到半個(gè)月后榄攀,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡金句,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年檩赢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片违寞。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贞瞒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出趁曼,到底是詐尸還是另有隱情军浆,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布挡闰,位于F島的核電站乒融,受9級(jí)特大地震影響掰盘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赞季,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一愧捕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧申钩,春花似錦次绘、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至义黎,卻和暖如春禾进,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背廉涕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工命迈, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人火的。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像淑倾,于是被迫代替她去往敵國(guó)和親馏鹤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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

  • Grand Central Dispatch(GCD)概要 我的博客鏈接 什么是GCD娇哆? 蘋(píng)果官方這么描述的:Gr...
    換個(gè)名字再說(shuō)閱讀 1,286評(píng)論 4 7
  • 最近頗花了一番功夫把多線程GCD人的一些用法總結(jié)出來(lái)湃累,一來(lái)幫自己鞏固一下知識(shí)、二來(lái)希望能幫到對(duì)這一塊還迷茫...
    人活一世閱讀 289評(píng)論 1 1
  • 一碍讨、GCD的API 1. Dispatch queue 在執(zhí)行處理時(shí)存在兩種Dispatch queue: 等待現(xiàn)...
    doudo閱讀 500評(píng)論 0 0
  • 1治力、上推用于概括總結(jié) 上推的應(yīng)用場(chǎng)景:和別人溝通達(dá)到自己的目的之后并且不想繼續(xù)聊下去的是 2、平行可以用去持續(xù)繼...
    lynn0閱讀 1,147評(píng)論 0 0
  • 有人說(shuō)我這樣的人不適合生活勃黍。 我不歡喜讓人腎上腺素飆升的一時(shí)的刺激宵统,也不好高騖遠(yuǎn)追求富裕抑或是隱士閑居,更不偏...
    枯墨荒筆閱讀 783評(píng)論 1 1