前言
嘿嘿嘿,精品。
之前寫(xiě)了一篇iOS多線程匯總排监,地址如下
http://www.reibang.com/p/2642138d6bef
這里對(duì)GCD進(jìn)行詳細(xì)補(bǔ)充
概述
全稱(chēng)是Grand Central Dispatch延蟹,可譯為“牛逼的中樞調(diào)度器”。
純C語(yǔ)言悼沈,提供了非常多強(qiáng)大的函數(shù)。
優(yōu)勢(shì)
GCD是蘋(píng)果公司為多核的并行運(yùn)算提出的解決方案
GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核)
GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程窿给、調(diào)度任務(wù)、銷(xiāo)毀線程)
程序員只需要告訴GCD想要執(zhí)行什么任務(wù)率拒,不需要編寫(xiě)任何線程管理代碼
任務(wù)和隊(duì)列
GCD中有兩個(gè)核心概念
- 任務(wù):執(zhí)行什么操作
- 隊(duì)列:用來(lái)存放任務(wù)
使用GCD兩個(gè)步驟
- 定制任務(wù)
- 確定想做的事
- 確定想做的事情,將任務(wù)添加到隊(duì)列中
- GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出崩泡,放到對(duì)應(yīng)的線程中執(zhí)行。
GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出猬膨,放到對(duì)應(yīng)的線程中執(zhí)行
任務(wù)的取出遵循隊(duì)列的FIFO原則:先進(jìn)先出角撞,后進(jìn)后出
執(zhí)行任務(wù)
- GCD中有兩個(gè)用來(lái)執(zhí)行任務(wù)的函數(shù)
- 用同步的方式執(zhí)行任務(wù)
// queue:隊(duì)列
// block:任務(wù)
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
- 用異步的方式執(zhí)行任務(wù)
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
- 同步和異步的區(qū)別
同步:只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開(kāi)啟新線程的能力
異步:可以在新的線程中執(zhí)行任務(wù)勃痴,具備開(kāi)啟新線程的能力
添加隊(duì)列
-
GCD的隊(duì)列可以分為兩大類(lèi)型
-
并發(fā)隊(duì)列(Concurrent Dispatch Queue)
- 可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開(kāi)啟多個(gè)線程同時(shí)執(zhí)行任務(wù))
- 并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
-
串行隊(duì)列(Serial Dispatch Queue)
- 讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行(一個(gè)任務(wù)執(zhí)行完畢后谒所,再執(zhí)行下一個(gè)任務(wù))
-
易混術(shù)語(yǔ)
有4個(gè)術(shù)語(yǔ)比較容易混淆:同步、異步沛申、并發(fā)劣领、串行
-
同步和異步主要影響:能不能開(kāi)啟新的線程
同步:在當(dāng)前線程中執(zhí)行任務(wù),不具備開(kāi)啟新線程的能力
異步:在新的線程中執(zhí)行任務(wù)铁材,具備開(kāi)啟新線程的能力
-
并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式
并發(fā):多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行
串行:一個(gè)任務(wù)執(zhí)行完畢后剖踊,再執(zhí)行下一個(gè)任務(wù)
并發(fā)隊(duì)列
GCD默認(rèn)已經(jīng)提供了全局的并發(fā)隊(duì)列庶弃,供整個(gè)應(yīng)用使用,不需要手動(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即可
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 獲得全局并發(fā)隊(duì)列
- 手動(dòng)創(chuàng)建并發(fā)隊(duì)列
- 參數(shù)1:隊(duì)列標(biāo)識(shí)
- 參數(shù)2:隊(duì)列類(lèi)型
dispatch_queue_t concurrentQueue = dispatch_queue_create("CONCURRENT", DISPATCH_QUEUE_CONCURRENT);
- 全局并發(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ì)列
-
GCD中獲得串行有兩種途徑
手動(dòng)創(chuàng)建串行隊(duì)列
使用dispatch_queue_create函數(shù)創(chuàng)建串行隊(duì)列
// "SERIAL" 是一個(gè)標(biāo)識(shí)符歇攻,可以自己填寫(xiě),通常填寫(xiě)com.公司的域名
dispatch_queue_t serialQueue = dispatch_queue_create("SERIAL", DISPATCH_QUEUE_SERIAL);
// 或者
dispatch_queue_t serialQueue = dispatch_queue_create("SERIAL", NULL);
dispatch_release(queue); // 非ARC需要釋放手動(dòng)創(chuàng)建的隊(duì)列
- 使用主隊(duì)列(跟主線程相關(guān)聯(lián)的隊(duì)列)
- 主隊(duì)列是GCD自帶的一種特殊的串行隊(duì)列
- 放在主隊(duì)列中的任務(wù)梆造,都會(huì)放到主線程中執(zhí)行
- 使用dispatch_get_main_queue()獲得主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
####各種隊(duì)列的執(zhí)行效果
![](http://upload-images.jianshu.io/upload_images/2595997-971676ca92a8a6fa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/2595997-cf291e35f32f7629.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 小知識(shí):同步函數(shù)立刻執(zhí)行缴守,異步函數(shù)會(huì)等待自身所存在方法結(jié)束后才執(zhí)行。
- 注意:
- 使用sync函數(shù)往當(dāng)前串行隊(duì)列中添加任務(wù)镇辉,會(huì)卡住當(dāng)前的串行隊(duì)列,叫做死鎖(有些像把串行隊(duì)列嵌套)(下面會(huì)做詳細(xì)說(shuō)明).屡穗。
#死鎖
------
#### 搞清線程(Thread)和隊(duì)列(Queue)的區(qū)別
網(wǎng)上一些講解關(guān)于GCD死鎖的文章,有一些非常明顯的錯(cuò)誤忽肛,比如:認(rèn)為死鎖的原因是線程阻塞造成的村砂,這是非常大的誤解,GCD死鎖的原因是隊(duì)列阻塞屹逛,而不是線程阻塞础废!
![](http://upload-images.jianshu.io/upload_images/2595997-e0dbda73028aa1b7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
在開(kāi)發(fā)中,我們會(huì)把block罕模,也就是我們想做的任務(wù)评腺,交給GCD函數(shù)。GCD函數(shù)會(huì)把任務(wù)放進(jìn)我們指定的隊(duì)列(Queue)淑掌,當(dāng)然GCD函數(shù)內(nèi)部不止是把任務(wù)放進(jìn)隊(duì)列蒿讥,還包括一些其他不為我們所知的操作。隊(duì)列遵循嚴(yán)格的先進(jìn)先出原則抛腕,同一個(gè)Queue中芋绸,最早入列的block,會(huì)最早被分配給線程執(zhí)行担敌。系統(tǒng)(“系統(tǒng)”指所有被蘋(píng)果黑盒封裝摔敛,未公開(kāi)源碼,我們不能得知的操作柄错,下同)會(huì)依據(jù)順序從隊(duì)列中取出block舷夺,并且交由線程執(zhí)行苦酱。GCD隊(duì)列只是組織待執(zhí)行任務(wù)的一個(gè)數(shù)據(jù)結(jié)構(gòu)封裝售貌,而線程,才是執(zhí)行任務(wù)的人疫萤。
#### 程序執(zhí)行順序
要往下面講颂跨,不得不回顧一個(gè)再基礎(chǔ)不過(guò)的知識(shí)點(diǎn),我想扯饶,這是每一個(gè)程序員恒削,入門(mén)就知道的超級(jí)簡(jiǎn)單的知識(shí)池颈。雖然它非常基礎(chǔ)钓丰,但是躯砰,這正是造成我們GCD死鎖的重要因素。很多困難的問(wèn)題携丁,它們背后隱藏的東西往往非常簡(jiǎn)單琢歇,因?yàn)槭挛镉肋h(yuǎn)不會(huì)脫離本質(zhì)。
讓我們來(lái)看看下面的這個(gè)C程序:
include <stdio.h>
void printFiveNumbers(){
printf("開(kāi)始執(zhí)行printFiveNumbers函數(shù)了\n");
for (int i = 0; i < 5; i++) {
printf("printFiveNumbers - %d\n",i);
}
printf("執(zhí)行完printFiveNumbers函數(shù)了\n");
}
//main函數(shù)是程序的入口
int main(){
printf("main函數(shù)開(kāi)始執(zhí)行了\n");
printFiveNumbers();
printf("main函數(shù)執(zhí)行完了\n");
return 0;
}
![](http://upload-images.jianshu.io/upload_images/2595997-4579d283d6031841.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
大家都知道梦鉴,運(yùn)行的結(jié)果是怎么樣了李茫,程序的入口是main函數(shù),于是Run這個(gè)程序后肥橙,馬上就會(huì)進(jìn)入main函數(shù)執(zhí)行魄宏,執(zhí)行了第一句打印后,會(huì)跳入printFiveNumbers這個(gè)函數(shù)執(zhí)行存筏,直到printFiveNumbers執(zhí)行完宠互,才會(huì)返回到main函數(shù)繼續(xù)執(zhí)行下一句。重點(diǎn)是:外層方法會(huì)等待內(nèi)層方法返回后方篮,再執(zhí)行下一句指令名秀。就好像把printFiveNumbers函數(shù)的所有語(yǔ)句,都復(fù)制粘貼到了main方法里一樣藕溅。
#### GCD死鎖的本質(zhì)
讓我們看看下面這個(gè)程序:
override func viewDidLoad() {
super.viewDidLoad()
print("Start (NSThread.currentThread())")
//GCD同步函數(shù)
dispatch_sync(dispatch_get_main_queue(), {
for i in 0...100{
print("(i) (NSThread.currentThread())")
}
})
print("End (NSThread.currentThread())")
}
![](http://upload-images.jianshu.io/upload_images/2595997-3b6780b255265357.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
這個(gè)程序就是典型的死鎖匕得,可以看到,只打印了“Start”一行巾表,就再也沒(méi)有響應(yīng)了汁掠,已經(jīng)造成了GCD死鎖。為什么會(huì)這樣呢集币?讓我們來(lái)解讀一下這段程序的運(yùn)行順序:首先會(huì)打印“Start”考阱,然后將主隊(duì)列和一個(gè)block傳入GCD同步函數(shù)dispatch_sync中,等待sync函數(shù)執(zhí)行鞠苟,直到它返回乞榨,才會(huì)執(zhí)行打印“End”的語(yǔ)句〉庇椋可是吃既,竟然沒(méi)有反應(yīng)了?block中的101個(gè)數(shù)字沒(méi)有被打印出來(lái)任何一個(gè)跨细,viewDidLoad()中的End也沒(méi)有被打印出來(lái)鹦倚。也就是說(shuō),block沒(méi)有得到執(zhí)行的機(jī)會(huì)冀惭,viewDidLoad也沒(méi)有繼續(xù)執(zhí)行下去震叙。為什么block不執(zhí)行呢掀鹅?因?yàn)関iewDidLoad也是執(zhí)行在主隊(duì)列的,它是正在被執(zhí)行的任務(wù)媒楼,也就是說(shuō)乐尊,viewDidLoad()是主隊(duì)列的隊(duì)頭。主隊(duì)列是串行隊(duì)列划址,任務(wù)不能并發(fā)執(zhí)行科吭,同時(shí)只能有一個(gè)任務(wù)在執(zhí)行,也就是隊(duì)頭的任務(wù)才能被出列執(zhí)行猴鲫。我們現(xiàn)在被執(zhí)行的任務(wù)是viewDidLoad()对人,然后我們又將block入列到同一個(gè)隊(duì)列,它比viewDidLoad()后入列拂共,遵循先進(jìn)先出的原理牺弄,它必須等到viewDidLoad()執(zhí)行完,才能被執(zhí)行宜狐。但是势告,dispatch_sync函數(shù)的特性是,等待block被執(zhí)行完畢抚恒,才會(huì)返回咱台,因此,只要block一天不被執(zhí)行俭驮,它就一天不返回回溺。我們知道,內(nèi)部方法不返回混萝,外部方法是不會(huì)執(zhí)行下一行命令的遗遵。不等到sync函數(shù)返回,viewDidLoad打死也不會(huì)執(zhí)行print End的語(yǔ)句逸嘀,因此车要,viewDidLoad()一直沒(méi)有執(zhí)行完畢。block在等待著viewDidLoad()執(zhí)行完畢崭倘,它才能上翼岁,sync函數(shù)在等待著block執(zhí)行完畢,它才能返回司光,viewDidLoad()在等待著sync函數(shù)返回琅坡,它才能執(zhí)行完畢。這樣的三方循環(huán)等待關(guān)系飘庄,就造成了死鎖脑蠕。
也許文字描述比較抽象购撼,我們?cè)賮?lái)配一幅圖:
![](http://upload-images.jianshu.io/upload_images/2595997-8ee6429e8fef13b6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
可以這么理解:每一個(gè)隊(duì)列跪削,有自己的執(zhí)行室谴仙,串行隊(duì)列的執(zhí)行室,只能容納一個(gè)任務(wù)碾盐,并發(fā)隊(duì)列的執(zhí)行室晃跺,可以同時(shí)容納若干個(gè)任務(wù)。隊(duì)頭的任務(wù)毫玖,只要執(zhí)行室有空位掀虎,就會(huì)被放入執(zhí)行室執(zhí)行。viewDidLoad任務(wù)在執(zhí)行中付枫,我們的主隊(duì)列又是串行隊(duì)列烹玉,執(zhí)行室只能容納一個(gè)任務(wù),那么隊(duì)頭的block就需要等待viewDidLoad執(zhí)行完畢才能進(jìn)入執(zhí)行室阐滩,那么就造成了二打,viewDidLoad永遠(yuǎn)不會(huì)執(zhí)行完畢,block永遠(yuǎn)不能執(zhí)行掂榔。
![](http://upload-images.jianshu.io/upload_images/2595997-9ab1702b34fc39b9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
sync函數(shù)永遠(yuǎn)不能返回继效,最終,就是GCD死鎖装获。
- 那么我們可以總結(jié)出GCD被阻塞(blocking)的原因有以下兩點(diǎn):
1. GCD函數(shù)未返回瑞信,會(huì)阻塞正在執(zhí)行的任務(wù)
2. 隊(duì)列的執(zhí)行室容量太小,在執(zhí)行室有空位之前穴豫,會(huì)阻塞同一個(gè)隊(duì)列中在等待的任務(wù)
- 注意:阻塞(blocking)和死鎖(deadlock)是不同的意思凡简,阻塞表示需要等待A事件完成后才能完成B事件,稱(chēng)作A會(huì)阻塞B精肃,通俗來(lái)講就是強(qiáng)制等待的意思潘鲫。而死鎖表示由于某些互相阻塞,也就是互相的強(qiáng)制等待肋杖,形成了閉環(huán)溉仑,導(dǎo)致大家永遠(yuǎn)互相阻塞下去了,Always and Forever状植,也就是死鎖浊竟。
#### 解決GCD死鎖
我們已經(jīng)有結(jié)論,造成GCD死鎖津畸,是由于同時(shí)具備以下兩點(diǎn)因素:
1. GCD函數(shù)未返回振定,會(huì)阻塞正在執(zhí)行的任務(wù)
2. 隊(duì)列的執(zhí)行室容量太小,在執(zhí)行室有空位之前肉拓,會(huì)阻塞同一個(gè)隊(duì)列中在等待的任務(wù)
死鎖是由于阻塞閉環(huán)造成的后频,那么我們只用消除其中一個(gè)因素,就能打破這個(gè)閉環(huán),避免死鎖卑惜。
#######方法1: 解決GCD函數(shù)未返回造成的阻塞
- 先提出下面兩個(gè)知識(shí)點(diǎn):
- dispatch_sync是同步函數(shù)膏执,不具備開(kāi)啟新線程的能力,交給它的block露久,只會(huì)在當(dāng)前線程執(zhí)行更米,不論你傳入的是串行隊(duì)列還是并發(fā)隊(duì)列,并且毫痕,它一定會(huì)等待block被執(zhí)行完畢才返回征峦。
- dispatch_async是異步函數(shù),具備開(kāi)啟新線程的能力消请,但是不一定會(huì)開(kāi)啟新縣城栏笆,交給它的block,可能在任何線程執(zhí)行臊泰,開(kāi)發(fā)者無(wú)法控制竖伯,是GCD底層在控制。它會(huì)立即返回因宇,不會(huì)等待block被執(zhí)行七婴。
- 注意:以上兩個(gè)知識(shí)點(diǎn),有例外察滑,那就是當(dāng)你傳入的是主隊(duì)列打厘,那兩個(gè)函數(shù)都一定會(huì)安排block在主線程執(zhí)行。記住贺辰,主隊(duì)列是最特殊的隊(duì)列
只要看懂了以上兩個(gè)知識(shí)點(diǎn)户盯,大家就知道,sync函數(shù)未返回會(huì)造成阻塞饲化,只要換成aysnc函數(shù)莽鸭,就會(huì)立即返回,而不會(huì)等待block執(zhí)行吃靠,那么GCD函數(shù)未返回這個(gè)阻塞因素就會(huì)被解決掉硫眨。不用大家也不要盲目的換函數(shù),畢竟兩個(gè)函數(shù)是有不同之處的巢块,要考慮實(shí)際期望礁阁。
#######方法2: 解決隊(duì)列(Queue)阻塞
解決隊(duì)列阻塞,有兩種方法:
1. 為隊(duì)列的執(zhí)行室擴(kuò)容族奢,讓它可以并發(fā)執(zhí)行多個(gè)任務(wù)姥闭,那么就不會(huì)因?yàn)锳任務(wù),造成B任務(wù)被阻塞了越走。
2. 把A和B任務(wù)放在兩個(gè)不同的隊(duì)列中棚品,A就再也沒(méi)有機(jī)會(huì)阻塞B了。因?yàn)槊總€(gè)隊(duì)列都有自己的執(zhí)行室。
首先來(lái)說(shuō)第一個(gè)思路铜跑,如何為隊(duì)列的執(zhí)行室擴(kuò)容呢门怪?我們當(dāng)然沒(méi)有辦法為執(zhí)行室擴(kuò)容,但是我們可以選擇用容量大的隊(duì)列疼进。使用并發(fā)隊(duì)列替代串行隊(duì)列。因?yàn)椴l(fā)隊(duì)列的執(zhí)行室可以同時(shí)容納若干任務(wù)
再來(lái)說(shuō)第二個(gè)思路秧廉,我們來(lái)看代碼:
override func viewDidLoad() {
super.viewDidLoad()
print("Start (NSThread.currentThread())")
let serialQueue = dispatch_queue_create("這是一個(gè)串行隊(duì)列", DISPATCH_QUEUE_SERIAL)
dispatch_sync(serialQueue, {
for i in 0...100{
print("(i) (NSThread.currentThread())")
}
})
print("End (NSThread.currentThread())")
}
![](http://upload-images.jianshu.io/upload_images/2595997-85381d8d2ab036b5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我們自己新建了一個(gè)串行隊(duì)列伞广,將block放入自己的串行隊(duì)列,不再和viewDidLoad()處于一個(gè)隊(duì)列疼电,解決了隊(duì)列阻塞嚼锄,因此避免了死鎖問(wèn)題。
網(wǎng)上有一些帖子說(shuō)“在主線程使用sync函數(shù)就會(huì)造成死鎖”或者“在主線程使用sync函數(shù)蔽豺,同時(shí)傳入串行隊(duì)列就會(huì)死鎖”区丑,都是非常錯(cuò)誤的觀念,希望大家能夠真正理解GCD死鎖的原理修陡,而不是死記硬背沧侥。
# 其他
-----
GCD還有一些其他用法,這里也都提一下魄鸦。
#### 線程間通信示例
從子線程回到主線程
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行耗時(shí)的異步操作...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主線程宴杀,執(zhí)行UI刷新操作
});
});
#### 快速迭代
快速遍歷,當(dāng)我們需要遍歷一些耗時(shí)操作拾因,需要它們同時(shí)進(jìn)行旺罢,可以使用dispatch_apply,比如下面的例子是把一個(gè)文件夾中的所有文件剪切到另一個(gè)文件夾中绢记,所有文件近乎同時(shí)剪切扁达。
-
(void)apply
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSString *from = @"/Users/Ostkaka/Desktop/From";
NSString *to = @"/Users/Ostkaka/Desktop/To";NSFileManager *mgr = [NSFileManager defaultManager];
NSArray *subpaths = [mgr subpathsAtPath:from];dispatch_apply(subpaths.count, queue, ^(size_t index) {
NSString *subpath = subpaths[index];
NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
// 剪切
[mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];NSLog(@"%@---%@", [NSThread currentThread], subpath);
});
}
#### 柵欄
如果不加dispatch_barrier_async()這行代碼,會(huì)開(kāi)辟四條線程無(wú)序執(zhí)行蠢熄,添加之后會(huì)先執(zhí)行它之前的跪解,結(jié)束后執(zhí)行它之后的。
不過(guò)注意一點(diǎn)签孔,這里一定要?jiǎng)?chuàng)建新的并發(fā)隊(duì)列惠遏,用global是不可以的。
-
(void)barrier
{
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
}
#### 延時(shí)執(zhí)行
- iOS常見(jiàn)的延時(shí)執(zhí)行有3種方式
- 調(diào)用NSObject的方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 2秒后再調(diào)用self的run方法
- 使用GCD函數(shù)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后異步執(zhí)行這里的代碼...
});
- NSTimer
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
#### 隊(duì)列組
- 有這么1種需求
- 首先:分別異步執(zhí)行2個(gè)耗時(shí)的操作
- 其次:等2個(gè)異步操作都執(zhí)行完畢后骏啰,再回到主線程執(zhí)行操作
- 如果想要快速高效地實(shí)現(xiàn)上述需求节吮,可以考慮用隊(duì)列組
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個(gè)耗時(shí)的異步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個(gè)耗時(shí)的異步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后,回到主線程...
});
#### 一次性代碼
使用dispatch_once函數(shù)能保證某段代碼在程序運(yùn)行過(guò)程中只被執(zhí)行1次
static dispatch_once_t onceToken; //記錄是否被執(zhí)行
dispatch_once(&onceToken, ^{
// 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)
});
#### 單例模式
- 作用
- 可以保證在程序運(yùn)行過(guò)程判耕,一個(gè)類(lèi)只有一個(gè)實(shí)例透绩,而且該實(shí)例易于供外界訪問(wèn),從而方便地控制了實(shí)例個(gè)數(shù),并節(jié)約系統(tǒng)資源
- 使用場(chǎng)合
- 在整個(gè)應(yīng)用程序中帚豪,共享一份資源(這份資源只需要?jiǎng)?chuàng)建初始化1次)
- 單例模式在ARC\MRC環(huán)境下的寫(xiě)法有所不同碳竟,需要編寫(xiě)2套不同的代碼
- 可以用宏判斷是否為ARC環(huán)境
if __has_feature(objc_arc)
// ARC
else
// MRC
endif
#### 單例模式(ARC)
// 在.m中保留一個(gè)全局的static的實(shí)例
static id _instance;
// 重寫(xiě)allocWithZone:方法,在這里創(chuàng)建唯一的實(shí)例(注意線程安全)
- (id)allocWithZone:(struct _NSZone *)zone
{
@synchronized(self) {
if (!_instance) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
提供1個(gè)類(lèi)方法讓外界訪問(wèn)唯一的實(shí)例
- (instancetype)sharedSoundTool
{
@synchronized(self) {
if (!_instance) {
_instance = [[self alloc] init];
}
}
return _instance;
}
實(shí)現(xiàn)copyWithZone:方法
- (id)copyWithZone:(struct _NSZone *)zone
{
return _instance;
}
#### 單例模式 – MRC
- MRC里狸臣,單例模式的實(shí)現(xiàn)(比ARC多了幾個(gè)步驟)
- 實(shí)現(xiàn)內(nèi)存管理方法
- (id)retain { return self; }
- (NSUInteger)retainCount { return 1; }
- (oneway void)release {}
- (id)autorelease { return self; }
# 心靈雞湯
------
禪房里莹桅,這個(gè)整日被嫉妒、浮躁烛亦、憂(yōu)慮所困擾的年輕人面對(duì)慈祥诈泼、超然的禪師,一股腦兒倒出了自己的不幸煤禽。禪師笑笑铐达,伸出右手,握成拳頭檬果,“你試試看瓮孙。”年輕人照做选脊『伎伲“再握得緊一些】疑叮”“再緊一些······”于是年輕人把拳頭握得越來(lái)越緊祈争。“感覺(jué)如何角寸?”禪師慈祥的問(wèn)道菩混。年輕人茫然的搖了搖頭。禪師說(shuō):好扁藕,現(xiàn)在可以上下動(dòng)了沮峡。
海的平凡是因?yàn)樗从谝稽c(diǎn)一滴,海的偉大是因?yàn)樗苋菁{千江萬(wàn)河,還有海鮮.
![](http://upload-images.jianshu.io/upload_images/2595997-bc7155a7fa32a65b.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)