無(wú)標(biāo)題文章

概述

這篇文章中揍瑟,我不會(huì)說(shuō)多線程是什么少欺、線程和進(jìn)程的區(qū)別喳瓣、多線程有什么用,當(dāng)然我也不會(huì)說(shuō)什么是串行赞别、什么是并行等問(wèn)題夫椭,這些我們應(yīng)該都知道的。

在 iOS 中其實(shí)目前有 4 套多線程方案氯庆,他們分別是:

Pthreads

NSThread

GCD

NSOperation & NSOperationQueue

所以接下來(lái)蹭秋,我會(huì)一一講解這些方案的使用方法和一些案例。在將這些內(nèi)容的時(shí)候堤撵,我也會(huì)順帶說(shuō)一些多線程周邊產(chǎn)品仁讨。比如: 線程同步、 延時(shí)執(zhí)行实昨、 單例模式 等等洞豁。

Pthreads

其實(shí)這個(gè)方案不用說(shuō)的,只是拿來(lái)充個(gè)數(shù),為了讓大家了解一下就好了丈挟。百度百科里是這么說(shuō)的:

POSIX線程(POSIX threads)刁卜,簡(jiǎn)稱(chēng)Pthreads,是線程的POSIX標(biāo)準(zhǔn)曙咽。該標(biāo)準(zhǔn)定義了創(chuàng)建和操縱線程的一整套API蛔趴。在類(lèi)Unix操作系統(tǒng)(Unix、Linux例朱、Mac OS X等)中,都使用Pthreads作為操作系統(tǒng)的線程箫荡。

簡(jiǎn)單地說(shuō)患民,這是一套在很多操作系統(tǒng)上都通用的多線程API伍茄,所以移植性很強(qiáng)(然并卵)婉弹,當(dāng)然在 iOS 中也是可以的。不過(guò)這是基于 c語(yǔ)言 的框架氯哮,使用起來(lái)這酸爽!感受一下:

OBJECTIVE-C

當(dāng)然第一步要包含頭文件

#import

然后創(chuàng)建線程喉钢,并執(zhí)行任務(wù)

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

pthread_t thread;

//創(chuàng)建一個(gè)線程并自動(dòng)執(zhí)行

pthread_create(&thread, NULL, start, NULL);

}

void *start(void *data) {

NSLog(@"%@", [NSThread currentThread]);

return NULL;

}

打印輸出:

2015-07-27 23:57:21.689 testThread[10616:2644653] {number = 2, name = (null)}

看代碼就會(huì)發(fā)現(xiàn)他需要 c語(yǔ)言函數(shù)姆打,這是比較蛋疼的,更蛋疼的是你需要手動(dòng)處理線程的各個(gè)狀態(tài)的轉(zhuǎn)換即管理生命周期肠虽,比如幔戏,這段代碼雖然創(chuàng)建了一個(gè)線程,但并沒(méi)有銷(xiāo)毀税课。

SWIFT

很遺憾闲延,在我目前的 swift1.2 中無(wú)法執(zhí)行這套方法,原因是這個(gè)函數(shù)需要傳入一個(gè)函數(shù)指針 CFunctionPointer類(lèi)型韩玩,但是目前 swift 無(wú)法將方法轉(zhuǎn)換成此類(lèi)型垒玲。聽(tīng)說(shuō) swift 2.0 引入一個(gè)新特性 @convention(c), 可以完成 Swift 方法轉(zhuǎn)換成 c 語(yǔ)言指針的。在這里可以看到

那么找颓,Pthreads 方案的多線程我就介紹這么多合愈,畢竟做 iOS 開(kāi)發(fā)幾乎不可能用到。但是如果你感興趣的話,或者說(shuō)想要自己實(shí)現(xiàn)一套多線程方案佛析,從底層開(kāi)始定制益老,那么可以去搜一下相關(guān)資料。

NSThread

這套方案是經(jīng)過(guò)蘋(píng)果封裝后的寸莫,并且完全面向?qū)ο蟮霓嗝取K阅憧梢灾苯硬倏鼐€程對(duì)象,非常直觀和方便储狭。但是,它的生命周期還是需要我們手動(dòng)管理慈参,所以這套方案也是偶爾用用驮配,比如 [NSThread currentThread],它可以獲取當(dāng)前線程類(lèi)猜绣,你就可以知道當(dāng)前線程的各種屬性,用于調(diào)試十分方便辣之。下面來(lái)看看它的一些用法怀估。

創(chuàng)建并啟動(dòng)

先創(chuàng)建線程類(lèi),再啟動(dòng)

OBJECTIVE-C

// 創(chuàng)建

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];

// 啟動(dòng)

[thread start];

SWIFT

//創(chuàng)建

let thread = NSThread(target: self, selector: "run:", object: nil)

//啟動(dòng)

thread.start()

創(chuàng)建并自動(dòng)啟動(dòng)

OBJECTIVE-C

[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];

SWIFT

NSThread.detachNewThreadSelector("run:", toTarget: self, withObject: nil)

使用 NSObject 的方法創(chuàng)建并自動(dòng)啟動(dòng)

OBJECTIVE-C

[self performSelectorInBackground:@selector(run:) withObject:nil];

SWIFT

很遺憾 too! 蘋(píng)果認(rèn)為 performSelector: 不安全,所以在 Swift 去掉了這個(gè)方法麻削。

Note: The performSelector: method and related selector-invoking methods are not imported in Swift because they are inherently unsafe.

其他方法

除了創(chuàng)建啟動(dòng)外呛哟,NSThread 還以很多方法榛鼎,下面我列舉一些常見(jiàn)的方法,當(dāng)然我列舉的并不完整黄鳍,更多方法大家可以去類(lèi)的定義里去看框沟。

OBJECTIVE-C

//取消線程

- (void)cancel;

//啟動(dòng)線程

- (void)start;

//判斷某個(gè)線程的狀態(tài)的屬性

@property (readonly, getter=isExecuting) BOOL executing;

@property (readonly, getter=isFinished) BOOL finished;

@property (readonly, getter=isCancelled) BOOL cancelled;

//設(shè)置和獲取線程名字

-(void)setName:(NSString *)n;

-(NSString *)name;

//獲取當(dāng)前線程信息

+ (NSThread *)currentThread;

//獲取主線程信息

+ (NSThread *)mainThread;

//使當(dāng)前線程暫停一段時(shí)間,或者暫停到某個(gè)時(shí)刻

+ (void)sleepForTimeInterval:(NSTimeInterval)time;

+ (void)sleepUntilDate:(NSDate *)date;

SWIFT

Swift的方法名字和OC的方法名都一樣梅垄,我就不浪費(fèi)空間列舉出來(lái)了。

其實(shí),NSThread 用起來(lái)也挺簡(jiǎn)單的貌虾,因?yàn)樗湍菐追N方法。同時(shí)袄膏,我們也只有在一些非常簡(jiǎn)單的場(chǎng)景才會(huì)用 NSThread, 畢竟它還不夠智能码党,不能優(yōu)雅地處理多線程中的其他高級(jí)概念揖盘。所以接下來(lái)要說(shuō)的內(nèi)容才是重點(diǎn)。

GCD

Grand Central Dispatch箕慧,聽(tīng)名字就霸氣颠焦。它是蘋(píng)果為多核的并行運(yùn)算提出的解決方案,所以會(huì)自動(dòng)合理地利用更多的CPU內(nèi)核(比如雙核似忧、四核),最重要的是它會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程饺著、調(diào)度任務(wù)幼衰、銷(xiāo)毀線程),完全不需要我們管理识椰,我們只需要告訴干什么就行。同時(shí)它使用的也是 c語(yǔ)言功咒,不過(guò)由于使用了 Block(Swift里叫做閉包)航瞭,使得使用起來(lái)更加方便章办,而且靈活藕届。所以基本上大家都使用 GCD 這套方案,老少咸宜踏兜,實(shí)在是居家旅行、殺人滅口疹尾,必備良藥。不好意思繁成,有點(diǎn)中二巾腕,咱們繼續(xù)侮穿。

任務(wù)和隊(duì)列

在 GCD 中回铛,加入了兩個(gè)非常重要的概念: 任務(wù) 和 隊(duì)列茵肃。

任務(wù):即操作捞附,你想要干什么鸟召,說(shuō)白了就是一段代碼,在 GCD 中就是一個(gè) Block跟继,所以添加任務(wù)十分方便舔糖。任務(wù)有兩種執(zhí)行方式: 同步執(zhí)行 和 異步執(zhí)行,他們之間的區(qū)別是 是否會(huì)創(chuàng)建新的線程辽聊。

同步執(zhí)行:只要是同步執(zhí)行的任務(wù)跟匆,都會(huì)在當(dāng)前線程執(zhí)行,不會(huì)另開(kāi)線程迹冤。

異步執(zhí)行:只要是異步執(zhí)行的任務(wù)泡徙,都會(huì)另開(kāi)線程莉兰,在別的線程執(zhí)行糖荒。

更新:

這里說(shuō)的并不準(zhǔn)確,同步(sync) 和 異步(async) 的主要區(qū)別在于會(huì)不會(huì)阻塞當(dāng)前線程泉孩,直到 Block 中的任務(wù)執(zhí)行完畢!

如果是 同步(sync) 操作句喷,它會(huì)阻塞當(dāng)前線程并等待 Block 中的任務(wù)執(zhí)行完畢唾琼,然后當(dāng)前線程才會(huì)繼續(xù)往下運(yùn)行。

如果是 異步(async)操作祭饭,當(dāng)前線程會(huì)直接往下執(zhí)行寺鸥,它不會(huì)阻塞當(dāng)前線程。

隊(duì)列:用于存放任務(wù)。一共有兩種隊(duì)列宰译, 串行隊(duì)列 和 并行隊(duì)列。

串行隊(duì)列 中的任務(wù)會(huì)根據(jù)隊(duì)列的定義 FIFO 的執(zhí)行缀拭,一個(gè)接一個(gè)的先進(jìn)先出的進(jìn)行執(zhí)行蛛淋。

更新:放到串行隊(duì)列的任務(wù)嘹悼,GCD 會(huì) FIFO(先進(jìn)先出) 地取出來(lái)一個(gè),執(zhí)行一個(gè)抖苦,然后取下一個(gè),這樣一個(gè)一個(gè)的執(zhí)行辩涝。

并行隊(duì)列 中的任務(wù)根據(jù)同步或異步有不同的執(zhí)行方式。雖然很繞,但請(qǐng)看下表:

更新:放到串行隊(duì)列的任務(wù),GCD 也會(huì) FIFO的取出來(lái)吝镣,但不同的是,它取出來(lái)一個(gè)就會(huì)放到別的線程拱撵,然后再取出來(lái)一個(gè)又放到另一個(gè)的線程。這樣由于取的動(dòng)作很快,忽略不計(jì)汇跨,看起來(lái),所有的任務(wù)都是一起執(zhí)行的。不過(guò)需要注意酷窥,GCD 會(huì)根據(jù)系統(tǒng)資源控制并行的數(shù)量,所以如果任務(wù)很多,它并不會(huì)讓所有任務(wù)同時(shí)執(zhí)行毅糟。

blob.png

創(chuàng)建隊(duì)列

主隊(duì)列:這是一個(gè)特殊的 串行隊(duì)列蝶防。什么是主隊(duì)列,大家都知道吧宗兼,它用于刷新 UI躏鱼,任何需要刷新 UI 的工作都要在主隊(duì)列執(zhí)行,所以一般耗時(shí)的任務(wù)都要放到別的線程執(zhí)行殷绍。

//OBJECTIVE-C

dispatch_queue_t queue = ispatch_get_main_queue();

//SWIFT

let queue = ispatch_get_main_queue()

自己創(chuàng)建的隊(duì)列:凡是自己創(chuàng)建的隊(duì)列都是 串行隊(duì)列染苛。其中第一個(gè)參數(shù)是標(biāo)識(shí)符,用于 DEBUG 的時(shí)候標(biāo)識(shí)唯一的隊(duì)列主到,可以為空。大家可以看xcode的文檔查看參數(shù)意義看锉。

更新:自己可以創(chuàng)建 串行隊(duì)列, 也可以創(chuàng)建 并行隊(duì)列糯彬〈覆樱看下面的代碼(代碼已更新),它有兩個(gè)參數(shù)叨粘,第一個(gè)上面已經(jīng)說(shuō)了鼻弧,第二個(gè)才是最重要的瓣履。

第二個(gè)參數(shù)用來(lái)表示創(chuàng)建的隊(duì)列是串行的還是并行的,傳入 DISPATCH_QUEUE_SERIAL 或 NULL 表示創(chuàng)建串行隊(duì)列。傳入 DISPATCH_QUEUE_CONCURRENT 表示創(chuàng)建并行隊(duì)列。

//OBJECTIVE-C

dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);

//SWIFT

let queue = dispatch_queue_create("tk.bourne.testQueue", nil);

全局并行隊(duì)列:這應(yīng)該是唯一一個(gè)并行隊(duì)列躏嚎,只要是并行任務(wù)一般都加入到這個(gè)隊(duì)列诈乒。

//OBJECTIVE-C

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//SWIFT

let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

創(chuàng)建任務(wù)

同步任務(wù):不會(huì)另開(kāi)線程 (SYNC)

OBJECTIVE-C

dispatch_sync(, ^{

//code here

NSLog(@"%@", [NSThread currentThread]);

});

SWIFT

dispatch_sync(, { () -> Void in

//code here

println(NSThread.currentThread())

})

異步任務(wù):會(huì)另開(kāi)線程 (ASYNC)

OBJECTIVE-C

dispatch_async(, ^{

//code here

NSLog(@"%@", [NSThread currentThread]);

});

SWIFT

dispatch_async(, { () -> Void in

//code here

println(NSThread.currentThread())

})

更新:

為了更好的理解同步和異步,和各種隊(duì)列的使用生闲,下面看兩個(gè)示例:

示例一:

以下代碼在主線程調(diào)用,結(jié)果是什么?

NSLog("之前 - %@", NSThread.currentThread())

dispatch_sync(dispatch_get_main_queue(), { () -> Void in

NSLog("sync - %@", NSThread.currentThread())

})

NSLog("之后 - %@", NSThread.currentThread())

答案:

只會(huì)打印第一句:之前 - {number = 1, name = main} 系宫,然后主線程就卡死了,你可以在界面上放一個(gè)按鈕碗短,你就會(huì)發(fā)現(xiàn)點(diǎn)不了了。

解釋?zhuān)?/p>

同步任務(wù)會(huì)阻塞當(dāng)前線程狂塘,然后把 Block 中的任務(wù)放到指定的隊(duì)列中執(zhí)行荸哟,只有等到 Block 中的任務(wù)完成后才會(huì)讓當(dāng)前線程繼續(xù)往下運(yùn)行像云。

那么這里的步驟就是:打印完第一句后底靠,dispatch_sync 立即阻塞當(dāng)前的主線程稻轨,然后把 Block 中的任務(wù)放到 main_queue 中竖慧,可以 main_queue 中的任務(wù)會(huì)被取出來(lái)放到主線程中執(zhí)行帚稠,但主線程這個(gè)時(shí)候已經(jīng)被阻塞了桐筏,所以 Block 中的任務(wù)就不能完成纸型,它不完成,dispatch_sync 就會(huì)一直阻塞主線程梅忌,這就是死鎖現(xiàn)象狰腌。導(dǎo)致主線程一直卡死。

示例二:

以下代碼會(huì)產(chǎn)生什么結(jié)果牧氮?

let queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL)

NSLog("之前 - %@", NSThread.currentThread())

dispatch_async(queue, { () -> Void in

NSLog("sync之前 - %@", NSThread.currentThread())

dispatch_sync(queue, { () -> Void in

NSLog("sync - %@", NSThread.currentThread())

})

NSLog("sync之后 - %@", NSThread.currentThread())

})

NSLog("之后 - %@", NSThread.currentThread())

答案:

2015-07-30 02:06:51.058 test[33329:8793087] 之前 - {number = 1, name = main}

2015-07-30 02:06:51.059 test[33329:8793356] sync之前 - {number = 2, name = (null)}

2015-07-30 02:06:51.059 test[33329:8793087] 之后 - {number = 1, name = main}

很明顯 sync - %@ 和 sync之后 - %@ 沒(méi)有被打印出來(lái)琼腔!這是為什么呢?我們?cè)賮?lái)分析一下:

分析:

我們按執(zhí)行順序一步步來(lái)哦:

使用 DISPATCH_QUEUE_SERIAL 這個(gè)參數(shù)踱葛,創(chuàng)建了一個(gè) 串行隊(duì)列丹莲。

打印出 之前 - %@ 這句。

dispatch_async 異步執(zhí)行尸诽,所以當(dāng)前線程不會(huì)被阻塞甥材,于是有了兩條線程,一條當(dāng)前線程繼續(xù)往下打印出 之后 - %@這句, 另一臺(tái)執(zhí)行 Block 中的內(nèi)容打印 sync之前 - %@ 這句性含。因?yàn)檫@兩條是并行的洲赵,所以打印的先后順序無(wú)所謂。

注意,高潮來(lái)了〉迹現(xiàn)在的情況和上一個(gè)例子一樣了芝发。dispatch_sync同步執(zhí)行,于是它所在的線程會(huì)被阻塞苛谷,一直等到 sync 里的任務(wù)執(zhí)行完才會(huì)繼續(xù)往下后德。于是 sync 就高興的把自己 Block 中的任務(wù)放到 queue 中,可誰(shuí)想 queue 是一個(gè)串行隊(duì)列抄腔,一次執(zhí)行一個(gè)任務(wù)瓢湃,所以 sync 的 Block 必須等到前一個(gè)任務(wù)執(zhí)行完畢,可萬(wàn)萬(wàn)沒(méi)想到的是 queue 正在執(zhí)行的任務(wù)就是被 sync 阻塞了的那個(gè)赫蛇。于是又發(fā)生了死鎖绵患。所以 sync 所在的線程被卡死了。剩下的兩句代碼自然不會(huì)打印悟耘。

隊(duì)列組

隊(duì)列組可以將很多隊(duì)列添加到一個(gè)組里落蝙,這樣做的好處是,當(dāng)這個(gè)組里所有的任務(wù)都執(zhí)行完了暂幼,隊(duì)列組會(huì)通過(guò)一個(gè)方法通知我們筏勒。下面是使用方法,這是一個(gè)很實(shí)用的功能旺嬉。

OBJECTIVE-C

//1.創(chuàng)建隊(duì)列組

dispatch_group_t group = dispatch_group_create();

//2.創(chuàng)建隊(duì)列

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//3.多次使用隊(duì)列組的方法執(zhí)行任務(wù), 只有異步方法

//3.1.執(zhí)行3次循環(huán)

dispatch_group_async(group, queue, ^{

for (NSInteger i = 0; i < 3; i++) {

NSLog(@"group-01 - %@", [NSThread currentThread]);

}

});

//3.2.主隊(duì)列執(zhí)行8次循環(huán)

dispatch_group_async(group, dispatch_get_main_queue(), ^{

for (NSInteger i = 0; i < 8; i++) {

NSLog(@"group-02 - %@", [NSThread currentThread]);

}

});

//3.3.執(zhí)行5次循環(huán)

dispatch_group_async(group, queue, ^{

for (NSInteger i = 0; i < 5; i++) {

NSLog(@"group-03 - %@", [NSThread currentThread]);

}

});

//4.都完成后會(huì)自動(dòng)通知

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

NSLog(@"完成 - %@", [NSThread currentThread]);

});

SWIFT

//1.創(chuàng)建隊(duì)列組

let group = dispatch_group_create()

//2.創(chuàng)建隊(duì)列

let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

//3.多次使用隊(duì)列組的方法執(zhí)行任務(wù), 只有異步方法

//3.1.執(zhí)行3次循環(huán)

dispatch_group_async(group, queue) { () -> Void in

for _ in 0.. Void in

for _ in 0.. Void in

for _ in 0.. Void in

NSLog("完成 - %@", NSThread.currentThread())

}

打印結(jié)果

2015-07-28 03:40:34.277 test[12540:3319271] group-03 - {number = 3, name = (null)}

2015-07-28 03:40:34.277 test[12540:3319146] group-02 - {number = 1, name = main}

2015-07-28 03:40:34.277 test[12540:3319146] group-02 - {number = 1, name = main}

2015-07-28 03:40:34.277 test[12540:3319271] group-03 - {number = 3, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319146] group-02 - {number = 1, name = main}

2015-07-28 03:40:34.278 test[12540:3319271] group-03 - {number = 3, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319271] group-03 - {number = 3, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319146] group-02 - {number = 1, name = main}

2015-07-28 03:40:34.277 test[12540:3319273] group-01 - {number = 2, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319271] group-03 - {number = 3, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319146] group-02 - {number = 1, name = main}

2015-07-28 03:40:34.278 test[12540:3319273] group-01 - {number = 2, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319146] group-02 - {number = 1, name = main}

2015-07-28 03:40:34.278 test[12540:3319273] group-01 - {number = 2, name = (null)}

2015-07-28 03:40:34.279 test[12540:3319146] group-02 - {number = 1, name = main}

2015-07-28 03:40:34.279 test[12540:3319146] group-02 - {number = 1, name = main}

2015-07-28 03:40:34.279 test[12540:3319146] 完成 - {number = 1, name = main}

這些就是 GCD 的基本功能管行,但是它的能力遠(yuǎn)不止這些,等講完 NSOperation 后邪媳,我們?cè)賮?lái)看看它的一些其他方面用途捐顷。而且,只要你想象力夠豐富雨效,你可以組合出更好的用法迅涮。

更新:關(guān)于GCD,還有兩個(gè)需要說(shuō)的:

func dispatch_barrier_async(_ queue: dispatch_queue_t, _ block: dispatch_block_t):

這個(gè)方法重點(diǎn)是你傳入的 queue徽龟,當(dāng)你傳入的 queue 是通過(guò) DISPATCH_QUEUE_CONCURRENT 參數(shù)自己創(chuàng)建的 queue 時(shí)叮姑,這個(gè)方法會(huì)阻塞這個(gè) queue(注意是阻塞 queue ,而不是阻塞當(dāng)前線程)据悔,一直等到這個(gè) queue 中排在它前面的任務(wù)都執(zhí)行完成后才會(huì)開(kāi)始執(zhí)行自己传透,自己執(zhí)行完畢后,再會(huì)取消阻塞屠尊,使這個(gè) queue 中排在它后面的任務(wù)繼續(xù)執(zhí)行旷祸。

如果你傳入的是其他的 queue, 那么它就和 dispatch_async 一樣了。

func dispatch_barrier_sync(_ queue: dispatch_queue_t, _ block: dispatch_block_t):

這個(gè)方法的使用和上一個(gè)一樣讼昆,傳入 自定義的并發(fā)隊(duì)列(DISPATCH_QUEUE_CONCURRENT),它和上一個(gè)方法一樣的阻塞 queue,不同的是 這個(gè)方法還會(huì) 阻塞當(dāng)前線程浸赫。

如果你傳入的是其他的 queue, 那么它就和 dispatch_sync 一樣了闰围。

NSOperation和NSOperationQueue

NSOperation 是蘋(píng)果公司對(duì) GCD 的封裝,完全面向?qū)ο蠹认浚允褂闷饋?lái)更好理解羡榴。 大家可以看到 NSOperation 和 NSOperationQueue 分別對(duì)應(yīng) GCD 的 任務(wù) 和 隊(duì)列 。操作步驟也很好理解:

將要執(zhí)行的任務(wù)封裝到一個(gè) NSOperation 對(duì)象中运敢。

將此任務(wù)添加到一個(gè) NSOperationQueue 對(duì)象中校仑。

然后系統(tǒng)就會(huì)自動(dòng)在執(zhí)行任務(wù)。至于同步還是異步传惠、串行還是并行請(qǐng)繼續(xù)往下看:

添加任務(wù)

值得說(shuō)明的是迄沫,NSOperation 只是一個(gè)抽象類(lèi),所以不能封裝任務(wù)卦方。但它有 2 個(gè)子類(lèi)用于封裝任務(wù)羊瘩。分別是:NSInvocationOperation 和 NSBlockOperation 。創(chuàng)建一個(gè) Operation 后盼砍,需要調(diào)用 start 方法來(lái)啟動(dòng)任務(wù)尘吗,它會(huì) 默認(rèn)在當(dāng)前隊(duì)列同步執(zhí)行。當(dāng)然你也可以在中途取消一個(gè)任務(wù)浇坐,只需要調(diào)用其 cancel 方法即可睬捶。

NSInvocationOperation : 需要傳入一個(gè)方法名。

OBJECTIVE-C

//1.創(chuàng)建NSInvocationOperation對(duì)象

NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

//2.開(kāi)始執(zhí)行

[operation start];

SWIFT

在 Swift 構(gòu)建的和諧社會(huì)里近刘,是容不下 NSInvocationOperation 這種不是類(lèi)型安全的敗類(lèi)的侧戴。蘋(píng)果如是說(shuō)。這里有相關(guān)解釋

NSBlockOperation

OBJECTIVE-C

//1.創(chuàng)建NSBlockOperation對(duì)象

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"%@", [NSThread currentThread]);

}];

//2.開(kāi)始任務(wù)

[operation start];

SWIFT

//1.創(chuàng)建NSBlockOperation對(duì)象

let operation = NSBlockOperation { () -> Void in

println(NSThread.currentThread())

}

//2.開(kāi)始任務(wù)

operation.start()

之前說(shuō)過(guò)這樣的任務(wù)跌宛,默認(rèn)會(huì)在當(dāng)前線程執(zhí)行酗宋。但是 NSBlockOperation 還有一個(gè)方法:addExecutionBlock: ,通過(guò)這個(gè)方法可以給 Operation 添加多個(gè)執(zhí)行 Block疆拘。這樣 Operation 中的任務(wù) 會(huì)并發(fā)執(zhí)行蜕猫,它會(huì) 在主線程和其它的多個(gè)線程 執(zhí)行這些任務(wù),注意下面的打印結(jié)果:

OBJECTIVE-C

//1.創(chuàng)建NSBlockOperation對(duì)象

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"%@", [NSThread currentThread]);

}];

//添加多個(gè)Block

for (NSInteger i = 0; i < 5; i++) {

[operation addExecutionBlock:^{

NSLog(@"第%ld次:%@", i, [NSThread currentThread]);

}];

}

//2.開(kāi)始任務(wù)

[operation start];

SWIFT

//1.創(chuàng)建NSBlockOperation對(duì)象

let operation = NSBlockOperation { () -> Void in

NSLog("%@", NSThread.currentThread())

}

//2.添加多個(gè)Block

for i in 0.. Void in

NSLog("第%ld次 - %@", i, NSThread.currentThread())

}

}

//2.開(kāi)始任務(wù)

operation.start()

打印輸出

2015-07-28 17:50:16.585 test[17527:4095467] 第2次 -{number = 1, name = main}

2015-07-28 17:50:16.585 test[17527:4095666] 第1次 -{number = 4, name = (null)}

2015-07-28 17:50:16.585 test[17527:4095665]{number = 3, name = (null)}

2015-07-28 17:50:16.585 test[17527:4095662] 第0次 -{number = 2, name = (null)}

2015-07-28 17:50:16.586 test[17527:4095666] 第3次 -{number = 4, name = (null)}

2015-07-28 17:50:16.586 test[17527:4095467] 第4次 -{number = 1, name = main}

NOTE:addExecutionBlock 方法必須在 start() 方法之前執(zhí)行哎迄,否則就會(huì)報(bào)錯(cuò):

‘*** -[NSBlockOperation addExecutionBlock:]: blocks cannot be added after the operation has started executing or finished'

NOTE:大家可能發(fā)現(xiàn)了一個(gè)問(wèn)題回右,為什么我在 Swift 里打印輸出使用 NSLog() 而不是 println() 呢?原因是使用 print() / println() 輸出的話漱挚,它會(huì)簡(jiǎn)單地使用 流(stream) 的概念翔烁,學(xué)過(guò) C++ 的都知道。它會(huì)把需要輸出的每個(gè)字符一個(gè)一個(gè)的輸出到控制臺(tái)旨涝。普通使用并沒(méi)有問(wèn)題蹬屹,可是當(dāng)多線程同步輸出的時(shí)候問(wèn)題就來(lái)了,由于很多 println() 同時(shí)打印,就會(huì)導(dǎo)致控制臺(tái)上的字符混亂的堆在一起慨默,而NSLog() 就沒(méi)有這個(gè)問(wèn)題贩耐。到底是什么樣子的呢?你可以把上面 NSLog() 改為 println() 厦取,然后一試便知潮太。 更多 NSLog() 與 println() 的區(qū)別看這里

自定義Operation

除了上面的兩種 Operation 以外,我們還可以自定義 Operation虾攻。自定義 Operation 需要繼承 NSOperation 類(lèi)铡买,并實(shí)現(xiàn)其 main() 方法,因?yàn)樵谡{(diào)用 start() 方法的時(shí)候霎箍,內(nèi)部會(huì)調(diào)用 main() 方法完成相關(guān)邏輯奇钞。所以如果以上的兩個(gè)類(lèi)無(wú)法滿足你的欲望的時(shí)候,你就需要自定義了朋沮。你想要實(shí)現(xiàn)什么功能都可以寫(xiě)在里面蛇券。除此之外,你還需要實(shí)現(xiàn) cancel() 在內(nèi)的各種方法樊拓。所以這個(gè)功能提供給高級(jí)玩家纠亚,我在這里就不說(shuō)了,等我需要用到時(shí)在研究它筋夏,到時(shí)候可能會(huì)再做更新蒂胞。

創(chuàng)建隊(duì)列

看過(guò)上面的內(nèi)容就知道,我們可以調(diào)用一個(gè) NSOperation 對(duì)象的 start() 方法來(lái)啟動(dòng)這個(gè)任務(wù)条篷,但是這樣做他們默認(rèn)是 同步執(zhí)行 的骗随。就算是 addExecutionBlock 方法,也會(huì)在 當(dāng)前線程和其他線程 中執(zhí)行赴叹,也就是說(shuō)還是會(huì)占用當(dāng)前線程鸿染。這是就要用到隊(duì)列 NSOperationQueue 了。而且乞巧,按類(lèi)型來(lái)說(shuō)的話一共有兩種類(lèi)型:主隊(duì)列涨椒、其他隊(duì)列。只要添加到隊(duì)列绽媒,會(huì)自動(dòng)調(diào)用任務(wù)的 start() 方法

主隊(duì)列

細(xì)心的同學(xué)就會(huì)發(fā)現(xiàn)蚕冬,每套多線程方案都會(huì)有一個(gè)主線程(當(dāng)然啦,說(shuō)的是iOS中是辕,像 pthread 這種多系統(tǒng)的方案并沒(méi)有囤热,因?yàn)?UI線程 理論需要每種操作系統(tǒng)自己定制)。這是一個(gè)特殊的線程获三,必須串行旁蔼。所以添加到主隊(duì)列的任務(wù)都會(huì)一個(gè)接一個(gè)地排著隊(duì)在主線程處理锨苏。

//OBJECTIVE-C

NSOperationQueue *queue = [NSOperationQueue mainQueue];

//SWIFT

let queue = NSOperationQueue.mainQueue()

其他隊(duì)列

因?yàn)橹麝?duì)列比較特殊,所以會(huì)單獨(dú)有一個(gè)類(lèi)方法來(lái)獲得主隊(duì)列牌芋。那么通過(guò)初始化產(chǎn)生的隊(duì)列就是其他隊(duì)列了蚓炬,因?yàn)橹挥羞@兩種隊(duì)列松逊,除了主隊(duì)列躺屁,其他隊(duì)列就不需要名字了。

注意:其他隊(duì)列的任務(wù)會(huì)在其他線程并行執(zhí)行经宏。

OBJECTIVE-C

//1.創(chuàng)建一個(gè)其他隊(duì)列

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

//2.創(chuàng)建NSBlockOperation對(duì)象

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"%@", [NSThread currentThread]);

}];

//3.添加多個(gè)Block

for (NSInteger i = 0; i < 5; i++) {

[operation addExecutionBlock:^{

NSLog(@"第%ld次:%@", i, [NSThread currentThread]);

}];

}

//4.隊(duì)列添加任務(wù)

[queue addOperation:operation];

SWIFT

//1.創(chuàng)建其他隊(duì)列

let queue = NSOperationQueue()

//2.創(chuàng)建NSBlockOperation對(duì)象

let operation = NSBlockOperation { () -> Void in

NSLog("%@", NSThread.currentThread())

}

//3.添加多個(gè)Block

for i in 0.. Void in

NSLog("第%ld次 - %@", i, NSThread.currentThread())

}

}

//4.隊(duì)列添加任務(wù)

queue.addOperation(operation)

打印輸出

2015-07-28 20:26:28.463 test[18622:4443534]{number = 5, name = (null)}

2015-07-28 20:26:28.463 test[18622:4443536] 第2次 -{number = 2, name = (null)}

2015-07-28 20:26:28.463 test[18622:4443535] 第0次 -{number = 4, name = (null)}

2015-07-28 20:26:28.463 test[18622:4443533] 第1次 -{number = 3, name = (null)}

2015-07-28 20:26:28.463 test[18622:4443534] 第3次 -{number = 5, name = (null)}

2015-07-28 20:26:28.463 test[18622:4443536] 第4次 -{number = 2, name = (null)}

OK, 這時(shí)應(yīng)該發(fā)問(wèn)了犀暑,大家將 NSOperationQueue 與 GCD的隊(duì)列 相比較就會(huì)發(fā)現(xiàn),這里沒(méi)有并行隊(duì)列烁兰,那如果我想要10個(gè)任務(wù)在其他線程串行的執(zhí)行怎么辦耐亏?

這就是蘋(píng)果封裝的妙處,你不用管串行沪斟、并行广辰、同步、異步這些名詞主之。NSOperationQueue 有一個(gè)參數(shù) maxConcurrentOperationCount 最大并發(fā)數(shù)择吊,用來(lái)設(shè)置最多可以讓多少個(gè)任務(wù)同時(shí)執(zhí)行。當(dāng)你把它設(shè)置為 1 的時(shí)候槽奕,他不就是串行了嘛几睛!

NSOperationQueue 還有一個(gè)添加任務(wù)的方法,- (void)addOperationWithBlock:(void (^)(void))block; 粤攒,這是不是和 GCD 差不多所森?這樣就可以添加一個(gè)任務(wù)到隊(duì)列中了,十分方便夯接。

NSOperation 有一個(gè)非常實(shí)用的功能焕济,那就是添加依賴(lài)。比如有 3 個(gè)任務(wù):A: 從服務(wù)器上下載一張圖片盔几,B:給這張圖片加個(gè)水印晴弃,C:把圖片返回給服務(wù)器。這時(shí)就可以用到依賴(lài)了:

OBJECTIVE-C

//1.任務(wù)一:下載圖片

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"下載圖片 - %@", [NSThread currentThread]);

[NSThread sleepForTimeInterval:1.0];

}];

//2.任務(wù)二:打水印

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"打水印? - %@", [NSThread currentThread]);

[NSThread sleepForTimeInterval:1.0];

}];

//3.任務(wù)三:上傳圖片

NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"上傳圖片 - %@", [NSThread currentThread]);

[NSThread sleepForTimeInterval:1.0];

}];

//4.設(shè)置依賴(lài)

[operation2 addDependency:operation1];? ? ? //任務(wù)二依賴(lài)任務(wù)一

[operation3 addDependency:operation2];? ? ? //任務(wù)三依賴(lài)任務(wù)二

//5.創(chuàng)建隊(duì)列并加入任務(wù)

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];

SWIFT

//1.任務(wù)一:下載圖片

let operation1 = NSBlockOperation { () -> Void in

NSLog("下載圖片 - %@", NSThread.currentThread())

NSThread.sleepForTimeInterval(1.0)

}

//2.任務(wù)二:打水印

let operation2 = NSBlockOperation { () -> Void in

NSLog("打水印? - %@", NSThread.currentThread())

NSThread.sleepForTimeInterval(1.0)

}

//3.任務(wù)三:上傳圖片

let operation3 = NSBlockOperation { () -> Void in

NSLog("上傳圖片 - %@", NSThread.currentThread())

NSThread.sleepForTimeInterval(1.0)

}

//4.設(shè)置依賴(lài)

operation2.addDependency(operation1)? ? //任務(wù)二依賴(lài)任務(wù)一

operation3.addDependency(operation2)? ? //任務(wù)三依賴(lài)任務(wù)二

//5.創(chuàng)建隊(duì)列并加入任務(wù)

let queue = NSOperationQueue()

queue.addOperations([operation3, operation2, operation1], waitUntilFinished: false)

打印結(jié)果

2015-07-28 21:24:28.622 test[19392:4637517] 下載圖片 -{number = 2, name = (null)}

2015-07-28 21:24:29.622 test[19392:4637515] 打水印 -{number = 3, name = (null)}

2015-07-28 21:24:30.627 test[19392:4637515] 上傳圖片 -{number = 3, name = (null)}

注意:不能添加相互依賴(lài)问欠,會(huì)死鎖肝匆,比如 A依賴(lài)B,B依賴(lài)A顺献。

可以使用 removeDependency 來(lái)解除依賴(lài)關(guān)系旗国。

可以在不同的隊(duì)列之間依賴(lài),反正就是這個(gè)依賴(lài)是添加到任務(wù)身上的注整,和隊(duì)列沒(méi)關(guān)系能曾。

其他方法

以上就是一些主要方法, 下面還有一些常用方法需要大家注意:

NSOperation

BOOL executing; //判斷任務(wù)是否正在執(zhí)行

BOOL finished; //判斷任務(wù)是否完成

void (^completionBlock)(void); //用來(lái)設(shè)置完成后需要執(zhí)行的操作

- (void)cancel; //取消任務(wù)

- (void)waitUntilFinished; //阻塞當(dāng)前線程直到此任務(wù)執(zhí)行完畢

NSOperationQueue

NSUInteger operationCount; //獲取隊(duì)列的任務(wù)數(shù)

- (void)cancelAllOperations; //取消隊(duì)列中所有的任務(wù)

- (void)waitUntilAllOperationsAreFinished; //阻塞當(dāng)前線程直到此隊(duì)列中的所有任務(wù)執(zhí)行完畢

[queue setSuspended:YES]; // 暫停queue

[queue setSuspended:NO]; // 繼續(xù)queue

好啦度硝,到這里差不多就講完了。當(dāng)然寿冕,我講的并不完整蕊程,可能有一些知識(shí)我并沒(méi)有講到,但作為常用方法驼唱,這些已經(jīng)足夠了藻茂。不過(guò)我在這里只是告訴你了一些方法的功能,只是怎么把他們用到合適的地方玫恳,就需要多多實(shí)踐了辨赐。下面我會(huì)說(shuō)一些關(guān)于多線程的案例,是大家更加什么地了解京办。

其他用法

在這部分掀序,我會(huì)說(shuō)一些和多線程知識(shí)相關(guān)的案例,可能有些很簡(jiǎn)單惭婿,大家早都知道的不恭,不過(guò)因?yàn)檫@篇文章講的是多線程嘛,所以應(yīng)該盡可能的全面嘛财饥。還有就是换吧,我會(huì)盡可能的使用多種方法實(shí)現(xiàn),讓大家看看其中的區(qū)別佑力。

線程同步

所謂線程同步就是為了防止多個(gè)線程搶奪同一個(gè)資源造成的數(shù)據(jù)安全問(wèn)題式散,所采取的一種措施。當(dāng)然也有很多實(shí)現(xiàn)方法打颤,請(qǐng)往下看:

互斥鎖 :給需要同步的代碼塊加一個(gè)互斥鎖暴拄,就可以保證每次只有一個(gè)線程訪問(wèn)此代碼塊。

OBJECTIVE-C

@synchronized(self) {

//需要執(zhí)行的代碼塊

}

SWIFT

objc_sync_enter(self)

//需要執(zhí)行的代碼塊

objc_sync_exit(self)

同步執(zhí)行 :我們可以使用多線程的知識(shí)编饺,把多個(gè)線程都要執(zhí)行此段代碼添加到同一個(gè)串行隊(duì)列乖篷,這樣就實(shí)現(xiàn)了線程同步的概念。當(dāng)然這里可以使用 GCD 和 NSOperation 兩種方案透且,我都寫(xiě)出來(lái)撕蔼。

OBJECTIVE-C

//GCD

//需要一個(gè)全局變量queue,要讓所有線程的這個(gè)操作都加到一個(gè)queue中

dispatch_sync(queue, ^{

NSInteger ticket = lastTicket;

[NSThread sleepForTimeInterval:0.1];

NSLog(@"%ld - %@",ticket, [NSThread currentThread]);

ticket -= 1;

lastTicket = ticket;

});

//NSOperation & NSOperationQueue

//重點(diǎn):1. 全局的 NSOperationQueue, 所有的操作添加到同一個(gè)queue中

//? ? ? 2. 設(shè)置 queue 的 maxConcurrentOperationCount 為 1

//? ? ? 3. 如果后續(xù)操作需要Block中的結(jié)果秽誊,就需要調(diào)用每個(gè)操作的waitUntilFinished鲸沮,阻塞當(dāng)前線程,一直等到當(dāng)前操作完成锅论,才允許執(zhí)行后面的讼溺。waitUntilFinished 要在添加到隊(duì)列之后!

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

NSInteger ticket = lastTicket;

[NSThread sleepForTimeInterval:1];

NSLog(@"%ld - %@",ticket, [NSThread currentThread]);

ticket -= 1;

lastTicket = ticket;

}];

[queue addOperation:operation];

[operation waitUntilFinished];

//后續(xù)要做的事

SWIFT

這里的 swift 代碼最易,我就不寫(xiě)了怒坯,因?yàn)槊烤涠家粯屿庞皇钦Z(yǔ)法不同而已,照著 OC 的代碼就能寫(xiě)出 Swift 的剔猿。這篇文章已經(jīng)老長(zhǎng)老長(zhǎng)了视译,我就不浪費(fèi)篇幅了,又不是高中寫(xiě)作文归敬。

延遲執(zhí)行

所謂延遲執(zhí)行就是延時(shí)一段時(shí)間再執(zhí)行某段代碼酷含。下面說(shuō)一些常用方法。

perform

OBJECTIVE-C

// 3秒后自動(dòng)調(diào)用self的run:方法弄慰,并且傳遞參數(shù):@"abc"

[self performSelector:@selector(run:) withObject:@"abc" afterDelay:3];

SWIFT

之前就已經(jīng)說(shuō)過(guò)第美,Swift 里去掉了這個(gè)方法蝶锋。

GCD

可以使用 GCD 中的 dispatch_after 方法陆爽,OC 和 Swift 都可以使用,這里只寫(xiě) OC 的扳缕,Swift 的是一樣的慌闭。

OBJECTIVE-C

// 創(chuàng)建隊(duì)列

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 設(shè)置延時(shí),單位秒

double delay = 3;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{

// 3秒后需要執(zhí)行的任務(wù)

});

NSTimer

NSTimer 是iOS中的一個(gè)計(jì)時(shí)器類(lèi)躯舔,除了延遲執(zhí)行還有很多用法驴剔,不過(guò)這里直說(shuō)延遲執(zhí)行的用法。同樣只寫(xiě) OC 版的粥庄,Swift 也是相同的丧失。

OBJECTIVE-C

1

[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run:) userInfo:@"abc" repeats:NO];

單例模式

至于什么是單例模式,我也不多說(shuō)惜互,我只說(shuō)說(shuō)一般怎么實(shí)現(xiàn)布讹。在 Objective-C 中,實(shí)現(xiàn)單例的方法已經(jīng)很具體了训堆,雖然有別的方法描验,但是一般都是用一個(gè)標(biāo)準(zhǔn)的方法了,下面來(lái)看看坑鱼。

OBJECTIVE-C

@interface Tool : NSObject

+ (instancetype)sharedTool;

@end

@implementation Tool

static id _instance;

+ (instancetype)sharedTool {

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

_instance = [[Tool alloc] init];

});

return _instance;

}

@end

這里之所以將單例模式膘流,是因?yàn)槠渲杏玫搅?GCD 的 dispatch_once 方法。下面看 Swift 中的單例模式鲁沥,在Swift中單例模式非常簡(jiǎn)單呼股!想知道怎么從 OC 那么復(fù)雜的方法變成下面的寫(xiě)法的,請(qǐng)看這里

SWIFT

class Tool: NSObject {

static let sharedTool = Tool()

// 私有化構(gòu)造方法画恰,阻止其他對(duì)象使用這個(gè)類(lèi)的默認(rèn)的'()'構(gòu)造方法

private override init() {}

}

從其他線程回到主線程的方法

我們都知道在其他線程操作完成后必須到主線程更新UI彭谁。所以,介紹完所有的多線程方案后阐枣,我們來(lái)看看有哪些方法可以回到主線程马靠。

NSThread

//Objective-C

[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];

//Swift

//swift 取消了 performSelector 方法奄抽。

GCD

//Objective-C

dispatch_async(dispatch_get_main_queue(), ^{

});

//Swift

dispatch_async(dispatch_get_main_queue(), { () -> Void in

})

NSOperationQueue

//Objective-C

[[NSOperationQueue mainQueue] addOperationWithBlock:^{

}];

//Swift

NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市甩鳄,隨后出現(xiàn)的幾起案子逞度,更是在濱河造成了極大的恐慌,老刑警劉巖妙啃,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件档泽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡揖赴,警方通過(guò)查閱死者的電腦和手機(jī)馆匿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)燥滑,“玉大人渐北,你說(shuō)我怎么就攤上這事∶。” “怎么了赃蛛?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)搀菩。 經(jīng)常有香客問(wèn)我呕臂,道長(zhǎng),這世上最難降的妖魔是什么肪跋? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任歧蒋,我火速辦了婚禮,結(jié)果婚禮上州既,老公的妹妹穿的比我還像新娘谜洽。我一直安慰自己,他們只是感情好易桃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布褥琐。 她就那樣靜靜地躺著,像睡著了一般晤郑。 火紅的嫁衣襯著肌膚如雪敌呈。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天造寝,我揣著相機(jī)與錄音磕洪,去河邊找鬼。 笑死诫龙,一個(gè)胖子當(dāng)著我的面吹牛析显,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播签赃,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谷异,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼分尸!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起歹嘹,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤箩绍,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后尺上,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體材蛛,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年怎抛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了卑吭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡马绝,死狀恐怖豆赏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情迹淌,我是刑警寧澤河绽,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站唉窃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏纹笼。R本人自食惡果不足惜纹份,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望廷痘。 院中可真熱鬧蔓涧,春花似錦、人聲如沸笋额。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)兄猩。三九已至茉盏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間枢冤,已是汗流浹背鸠姨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淹真,地道東北人讶迁。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像核蘸,于是被迫代替她去往敵國(guó)和親巍糯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子啸驯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • 在這篇文章中,我將為你整理一下 iOS 開(kāi)發(fā)中幾種多線程方案祟峦,以及其使用方法和注意事項(xiàng)坯汤。當(dāng)然也會(huì)給出幾種多線程的案...
    張戰(zhàn)威ican閱讀 603評(píng)論 0 0
  • NSThread 第一種:通過(guò)NSThread的對(duì)象方法 NSThread *thread = [[NSThrea...
    攻城獅GG閱讀 801評(píng)論 0 3
  • 一、前言 上一篇文章iOS多線程淺匯-原理篇中整理了一些有關(guān)多線程的基本概念搀愧。本篇博文介紹的是iOS中常用的幾個(gè)多...
    nuclear閱讀 2,050評(píng)論 6 18
  • 喜歡跑步惰聂,喜歡一個(gè)人圍著操場(chǎng)一圈又一圈的揮灑汗水,耳機(jī)里的音樂(lè)響起咱筛,這是我一個(gè)人的世界搓幌。 最開(kāi)始跑步是因?yàn)橄霚p肥,...
    貝希子閱讀 413評(píng)論 4 8
  • 情商比智商更重要 智慧比聰明更重要 創(chuàng)業(yè)者多用心 少用腦 不要比聰明 要多用心
    北陌塵閱讀 152評(píng)論 0 0