前言:這篇GCD的博文是本人閱讀了很多海內外大神的關於GCD的文章,以及結合之前自己對GCD的粗淺的認識,然后取其精華,去其槽粕,綜合起來的筆記,而且是盡可能的以通熟易懂的並且是正確的理論論述方式呈現給讀者,同時也是將大神博客中有的深澀的理論理解得通熟易懂之后再轉述給讀者,這已經是盡可能的讓讀者可以更深入得去理解和掌握多線程的知識以及GCD的使用技術。最后的附錄中,我將會給出所有本人閱讀的大神寫的關於多線程或者是GCD的文章鏈接,大家感興趣的,可以去參考和學習。也許,看我的這篇就夠了,因為我就是參考他們的,嘻嘻。👻
本篇博文:持續更新,還未更新完。
內容大綱:
相關基本概念
1、什么是GCD?
2、任務(Task)
3、隊列(Queue)
4、隊列的類型(Queue Types)
5、線程
6、總結一下同時使用隊列和線程
GCD技術淺談
1、延遲
2、正確創建dispatch_time_t
3、dispatch_suspend 不等於 "立即停止隊列的運行"
4、避免死鎖
5、GCD信號量
6、GCD的計時器
附錄:很好的GCD學習的網頁鏈接
------------------------------------------
另外呢,本人對GCD常用的API接口進行了簡易封裝,用起來很簡單,代碼可讀性大大提高了,github網址是:https://github.com/HeYang123456789/HYGCD。
如果接的好,記得在github上點個贊呀。
好了,我就不為自己打廣告了。下面請讀者認真學習下面的內容吧。
相關基本概念
1、什么是GCD?
iOS實現提供實現多線程的方案有:NSThread、NSOperation、NSInvocationOperation、pthread、GCD。
在iOS所有實現多線程的方案中,GCD應該是最有魅力的,而且使用起來也是最方便的,因為GCD是蘋果公司為多核的並行運算提出的解決方案。
GCD是Grand Central Dispatch的簡稱,它是基於C語言的。使用GCD,我們不需要編寫線程代碼,其生命周期也不需要我們手動管理,定義想要執行的任務,然后添加到適當的調度隊列,也就是dispatch queue。GCD會負責創建線程和調度任務,系統直接提供線程管理。
2、任務(Task)
一段代碼塊,執行之后能夠實現的一段業務邏輯過程。一般用閉包或者叫block,來包含它。執行任務,就需要將這個閉包或者block交給隊列,然后讓隊列 先進先出(FIFO) 的順序取出來,放進線程中執行。
3、隊列(Queue)
我們需要了解隊列的概念,GCD提供了dispatch queues來處理代碼塊,這些隊列管理所提供給GCD的任務並用FIFO順序執行這些任務。這樣才能保證第一個被添加到隊列里的任務會是隊列中第一個開始的任務,而第二個被添加的任務將第二個開始,如此直到隊列的終點。
串行(Serial)隊列
串行隊列 中的任務會根據隊列定義 先進先出(FIFO) 的順序取出來一個,執行一個,執行完了然后取出下一個任務,這樣一個一個的執行。
串行隊列圖:
並發(Concurrent)隊列
並發隊列 中的任務也會根據隊列定義 先進先出(FIFO)的順序一個個取出來,但是與串行隊列不同的是,並發隊列取出一個任務會放到別的線程中開始執行,先不管這個線程是同步線程還是異步線程,並發隊列並不是等線程中的任務執行完了而是取完了這個任務,就會立刻接着從並發隊列中取下一個任務放到別的線程中開始執行。由於取任務的速度很快,忽略不計,看起來,好像所有任務都是同時開始執行任務的。所以叫"並發隊列"。但是,任務有長有短,任務執行結束的順序你是無法確定的,有可能那個最短的任務先執行完畢了。
並發隊列圖:
補充:並發代碼的不同部分可以“同步”執行。然而,該怎樣發生或是否發生都取決於系統。多核設備通過並行來同時執行多個線程;然而,為了使單核設備也能實現這一點,它們必須先運行一個線程,執行一個上下文切換,然后運行另一個線程或進程。這通常發生地足夠快以致給我們並發執行地錯覺,如下圖所示:
4、隊列的類型(Queue Types)
系統提供的:
一個主隊列(main queue),是串行隊列。
和其它串行隊列一樣,這個隊列中的任務一次只能執行一個。然而,它能保證所有的任務都在主線程執行,而主線程是唯一可用於更新 UI 的線程。這個隊列就是用於發生消息給 UIView 或發送通知的。
在使用主線程串行隊列時,我們只通過dispatch_get_main_queue方法來獲取即可,我們不需要管理其內存問題。
四個全局調度隊列(Global Dispatch Queues),都是並行隊列
四個全局隊列有着不同的優先級:background、low、default 以及 high。要知道,Apple 的 API 也會使用這些隊列,所以你添加的任何任務都不會是這些隊列中唯一的任務。
在使用全局並發隊列時,我們只通過dispatch_get_global_queue方法來獲取即可,我們不需要管理其引用。
你也可以創建自己的串行隊列或並發隊列。
總結:這就是說,至少有五個隊列任你處置:主隊列、四個全局調度隊列,再加上任何你自己創建的隊列。
以上是調度隊列的大框架!
5、線程
線程分為兩種:Synchronous vs. Asynchronous 同步 vs. 異步。
GCD通過調用以下兩個函數分別創建同步任務和異步任務,放置隊列中,最后從隊列中取出交由線程執行:
這里會有兩個東西要說:同步函數和異步函數。我們的C語言基礎就學了函數,函數用大括號包含了一段代碼塊,當這個函數執行完畢就會返回,可以是返回void或者返回具體某個數據值,同時也代表着這個函數的執行完畢。那么線程中的函數,當然是包含了任務的,這個任務就是閉包或者叫block。同步函數和異步函數的區別就在,同步函數需要在完成了它預定的任務后才返回,而異步函數會立即返回,也就是說異步函數預定的任務會完成但不會等它完成就立即返回。
另外,同步線程是不具備創建子線程的能力的,一般情況下,同步線程的任務會在唯一的主線程中執行,也就是說唯一的(main)主線程就成了同步線程。而異步線程是具備創建子線程的能力的,一般情況下,異步線程就是(main)主線程以外的線程,不唯一,會有多個,具體多少個,具體是哪些線程,如果是通過GCD創建,全部由GCD決定。關於能創建的子線程的最大個數,這個就需要根據設備的CPU的能力了,比如下面是本人用真機,通過使用GCD創建了多個異步任務,並添加進並發隊列執行測試的結果:
可以發現,我創建了8個異步任務,GCD根據設備的極限,創建了三個子線程,分別是線程2、3、4,然后由它們隨機執行任務。其中線程4執行了四個任務,線程2和線程3分別執行了兩個任務。
那么,同步還是異步,這兩個術語是需要多個函數任務之間對比來論述的,這樣會更好理解。比如說同步,當線程執行一個同步函數任務的時候,這個線程會停下來,也就是所謂的"當前這個線程阻塞"了,當前線程需要等待這個函數執行完畢返回了值,也表示這個函數中的任務完全執行完畢了,這個線程才會繼續執行下去,去執行下一個函數任務,所以這樣的線程也叫同步線程。而異步函數所在的線程就不一樣了,線程開始執行一個異步函數任務的時候,因為異步函數立即返回了,雖然這個函數任務可能還沒執行完畢,但是返回了,這個線程就會繼續執行下一個函數任務,由於這個過程很快,快到可以忽略不計任務執行開始的先后順序,快到感覺好像都是同時開始執行任務的,所謂的"異步線程同時執行多個任務"就是這么來的。
注意:
不要將耗時操作放在主線程中,凡是跟UI相關的操作都是放在主線中處理
耗時操作應該放在子線程(后台線程,非主線程)
6、總結一下同時使用隊列和線程
好,下面我們來看看這個圖,可能會和你們別處看的有點不一樣,這是本人根據上面理論進行了適當的修改:
6-1、串行隊列+同步:
假設一堆任務放在串行隊列中。
因為串行隊列是先進先出FIFO隊列,而且串行隊列是需要前面取出的一個任務執行完了之后,才接着取下一任務。
因為同步(sync)沒有開啟新線程的能力,同步函數會阻塞當前線程。
所以,這一堆任務,將會在串行隊列和main主線程中,
一個任務從串行隊列提交到這個主線程,在這個任務執行完畢了之后,才接着從串行隊列中取出下一個任務提交到main主線程中,就這樣按順序執行任務。
驗證代碼:
打印結果:
2016-03-13 15:51:04.970 多線程[25106:632356] 當前線程是:<NSThread: 0x7fe28bc025b0>{number = 1, name = main}
2016-03-13 15:51:06.974 多線程[25106:632356] 當前線程是:<NSThread: 0x7fe28bc025b0>{number = 1, name = main}
2016-03-13 15:51:08.979 多線程[25106:632356] 當前線程是:<NSThread: 0x7fe28bc025b0>{number = 1, name = main}
2016-03-13 15:51:10.980 多線程[25106:632356] 當前線程是:<NSThread: 0x7fe28bc025b0>{number = 1, name = main}
分析打印結果:每隔2秒執行一個任務,說明出現了阻塞,執行的線程是main主線程。
6-2、並發隊列+同步:
假設一堆任務放在並發隊列中。
因為並發隊列也是先進先出FIFO隊列,但是並發隊列是需要取出的一個任務,這個任務還沒執行完了,就立刻接着取下一任務。
雖然並發隊列也是先進先出FIFO隊列,但是取出的速度很快,可以忽略不計,就好像是同時取出所有的任務,因此叫"並發隊列"。
因為同步(sync)不具有開啟新線程的能力,同步函數會阻塞當前線程。
所以,這一堆任務,將會在並發隊列和main主線程中,
所有的任務會快速的提交到main主線程上,一個任務執行完畢之后接着開始執行下一個任務,就這樣按順序執行任務。
驗證代碼:
打印結果:
2016-03-13 16:02:51.566 多線程[25327:637330] 當前線程是:<NSThread: 0x7fe53b6039c0>{number = 1, name = main}
2016-03-13 16:02:53.570 多線程[25327:637330] 當前線程是:<NSThread: 0x7fe53b6039c0>{number = 1, name = main}
2016-03-13 16:02:55.575 多線程[25327:637330] 當前線程是:<NSThread: 0x7fe53b6039c0>{number = 1, name = main}
2016-03-13 16:02:57.577 多線程[25327:637330] 當前線程是:<NSThread: 0x7fe53b6039c0>{number = 1, name = main}
分析打印結果:每隔2秒執行一個任務,說明出現了阻塞,執行的線程是main主線程。
6-3、串行隊列+異步:
假設一堆任務放在串行隊列中。
因為串行隊列是先進先出FIFO隊列,而且串行隊列是需要前面取出的一個任務執行完了之后,才接着取下一任務。
因為異步(async)有開啟新線程的能力,異步函數不會阻塞當前線程。
所以,這一堆任務,將會在串行隊列和開啟的新線程中,
一個任務從串行隊列提交到這個主線程,在這個任務執行完畢了之后,才接着從串行隊列中取出任務提交到新線程中,就這樣按順序執行任務。
驗證代碼:
打印結果:
2016-03-13 16:09:23.744 多線程[25460:640764] 當前線程是:<NSThread: 0x7fb08b638e00>{number = 2, name = (null)}
2016-03-13 16:09:25.750 多線程[25460:640764] 當前線程是:<NSThread: 0x7fb08b638e00>{number = 2, name = (null)}
2016-03-13 16:09:27.753 多線程[25460:640764] 當前線程是:<NSThread: 0x7fb08b638e00>{number = 2, name = (null)}
2016-03-13 16:09:29.757 多線程[25460:640764] 當前線程是:<NSThread: 0x7fb08b638e00>{number = 2, name = (null)}
分析打印結果:每隔2秒執行一個任務,說明串行隊列出現了等待前一個取出的任務執行完畢,執行的線程是新線程。
6-4、並發隊列+異步:
假設一堆任務放在並發隊列中。
因為並發隊列也是先進先出FIFO隊列,但是並發隊列是需要取出的一個任務,還沒執行完了,就立刻接着取下一任務。
雖然並發隊列也是先進先出FIFO隊列,但是取出的速度很快,可以忽略不計,就好像是同時取出所有的任務,因此叫"並發隊列"。
因為異步(async)具有開啟新線程的能力,異步函數不會阻塞當前線程。
所以,這一堆任務,將會在並發隊列和新線程中,
所有的任務會快速的提交到新線程上,如果當前新線程可能一時忙碌,並發隊列就可能把任務交到另一個新的線程中,就這樣多條線程的同時又是異步快速的執行任務。
驗證代碼:
打印結果:
2016-03-13 16:17:24.685 多線程[25648:645355] 當前線程是:<NSThread: 0x7f93a0605b70>{number = 1, name = main}
2016-03-13 16:17:28.693 多線程[25648:645477] 當前線程是:<NSThread: 0x7f93a0611a40>{number = 3, name = (null)}
2016-03-13 16:17:28.693 多線程[25648:645480] 當前線程是:<NSThread: 0x7f93a0620220>{number = 5, name = (null)}
2016-03-13 16:17:28.693 多線程[25648:645476] 當前線程是:<NSThread: 0x7f93a0706f60>{number = 2, name = (null)}
2016-03-13 16:17:28.693 多線程[25648:645478] 當前線程是:<NSThread: 0x7f93a0409ee0>{number = 4, name = (null)}
分析打印結果:主線程和新線程都休眠2秒,所以子線程的任務在4秒之后同時執行,隊列快速提交任務到各個線程,各個線程沒有阻塞任務。
GCD技術淺談
1、延遲
方法1:使用NSObject的api,延遲同步執行(不是延遲提交):
[self performSelector:@selector(myFunction) withObject:nil afterDelay:5.0];
補充:這個有延時執行取消的操作:
[NSObject cancelPreviousPerformRequestsWithTarget:self];//后面的參數就是執行performSelector的對象,在這里是self
方法2:使用NSTimer定時器(不是延遲提交)。
略。(補充,NSTimer有一個方法可以定時器精確到0.01秒,也就是每0.01秒可以打印出一條語句,當然這是沒有考慮屏幕刷新速率的)
方法3:使用dispatch_after方法異步延遲執行(是延遲提交):
CGFloat time = 5.0f;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
// time秒后異步執行這里的代碼...
});
補充:dispatch_after是延遲提交,不是延遲執行。
官方說明:
Enqueue a block for execution at the specified time.
Enqueue,就是入隊,指的就是將一個Block在特定的延時以后,加入到指定的隊列中,不是在特定的時間后立即運行!。
2、正確創建dispatch_time_t
用dispatch_after的時候就會用到dispatch_time_t變量,但是如何創建合適的時間呢?答案就是用dispatch_time函數,其原型如下:
dispatch_time_t dispatch_time ( dispatch_time_t when, int64_t delta );
第一個參數一般是DISPATCH_TIME_NOW,表示從現在開始。
第二個參數就是真正的延時的具體時間。
這里要特別注意的是,delta參數是“納秒!”,就是說,延時1秒的話,delta應該是“1000 000 000”,太長了,所以理所當然系統提供了常量,如下:
關鍵詞解釋:
NSEC:納秒。
USEC:微妙。
SEC:秒
PER:每
所以:
NSEC_PER_SEC,每秒有多少納秒。
USEC_PER_SEC,每秒有多少毫秒。(注意是指在納秒的基礎上)
NSEC_PER_USEC,每毫秒有多少納秒。
注意dispatch_time_t的第二個參數是納秒為單位的。所以當你第二個參數傳入1000 000 000ull或者是NSEC_PER_SEC就表示1秒了。
3、dispatch_suspend 不等於 "立即停止隊列的運行"
dispatch_suspend,dispatch_resume提供了“掛起、恢復”隊列的功能,簡單來說,就是可以暫停、恢復隊列上的任務。
但是這里的“掛起”,並不能保證可以立即停止隊列上正在運行的block,看如下例子:
打印結果:
本質:
suspend就是暫停取出隊列中的任務,暫停之前已經從隊列中取出的任務自然就讓它執行完為止了。
如果是並發隊列,然后暫停稍微晚一點,全部的任務都可能已經從隊列取出提交到線程中執行了。讀者可以自己敲代碼驗證。
4、避免死鎖
什么是死鎖?
其實就是任務互相等待。
下面看起來簡單,但是會出現死鎖哦,如果你的app出現了下面的情況,就會卡死不動的哦:
1、sync函數互相嵌套,產生死鎖
這個很好理解,updateUI1中的同步任務肯定比updateUI2先執行,因為只有updateUI1的任務執行完畢,updateUI2的任務被創建添加到線程中去並執行。
所以,首先我們可以確定updateUI1先執行,然后updateUI2后執行。
但是,因為updateUI1包含updateUI2作為任務,所以updateUI2執行完畢了,這樣updateUI1就算執行完畢了。如果updateUI2沒執行完,updateUI1就沒執行完畢。
可是,根據同步函數會阻塞的緣故,updateUI2需要等待updateUI1執行完畢了,才會輪到updateUI2執行。
就這樣,互相等待了。於是就出現死鎖了。
2、在main線程使用“同步”方法提交Block,必定會死鎖。
那么這個和前面一個情況是一樣的。其實程序邏輯就在當前運行的線程中執行,也就是主線程,主線程上的任務函數就是同步函數。
因為,你寫的一段一段代碼可不允許並發執行。如果並發執行的話,那你的代碼邏輯還不混亂了,你都不知道那段代碼先執行了,那段代碼后執行了。
也許你可能看不出來,那你可以直接加一句NSLog(@"當前線程是:%@",[NSThread current]);,由此可以證明當前執行的代碼就是在main線程中。
然后,你有在當前main線程中,創建一個在main線程中的同步函數任務。
這樣就和第一種情況一樣了。就出現死鎖了。
所以,盡量少用sync吧。
5、GCD信號量
關於GCD信號量示例一:
/**
* 當我們在處理一系列線程的時候,當數量達到一定量,在以前我們可能會選擇使用NSOperationQueue來處理並發控制,但如何在GCD中快速的控制並發呢?答案就是dispatch_semaphore,對經常做unix開發的人來講,我所介紹的內容可能就顯得非常入門級了,信號量在他們的多線程開發中再平常不過了。
信號量是一個整形值並且具有一個初始計數值,並且支持兩個操作:信號通知和等待。當一個信號量被信號通知,其計數會被增加。當一個線程在一個信號量上等待時,線程會被阻塞(如果有必要的話),直至計數器大於零,然后線程會減少這個計數。
在GCD中有三個函數是semaphore的操作,分別是:
dispatch_semaphore_create 創建一個semaphore
dispatch_semaphore_signal 發送一個信號
dispatch_semaphore_wait 等待信號
簡單的介紹一下這三個函數,第一個函數有一個整形的參數,我們可以理解為信號的總量,dispatch_semaphore_signal是發送一個信號,自然會讓信號總量加1,dispatch_semaphore_wait等待信號,當信號總量少於0的時候就會一直等待,否則就可以正常的執行,並讓信號總量減1,根據這樣的原理,我們便可以快速的創建一個並發控制來同步任務和有限資源訪問控制。
*
*
*/
//創建一個組
dispatch_group_t group = dispatch_group_create();
//信號初始總量為10
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
//獲取全局並發隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 100; i++)
{
//信號等待使信號總量-1,開始為10-1=9即繼續往下執行
//當循環遍歷到10次的時候,這個信號量等待就被執行了10次,第11次到這句信號量等待的代碼的時候,信號總量就變成-1,
//當前線程就卡住不會繼續執行了,也就是等待的樣子了
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//將一個並發任務關聯到group
dispatch_group_async(group, queue, ^{
//前面10個異步任務,打印出當前循環的i值
NSLog(@"%i",i);
//打印之后,前面10個異步任務,會停滯休眠2秒
sleep(2);
//停滯休眠2秒之后,10個異步任務同時對信號量加1,
//發送一個信號信號總量+1 如果+1前信號量小於1了即刻又可以開始執行之前的等待位置
dispatch_semaphore_signal(semaphore);
});
}
//等待group相關的所有任務執行完成才往下走
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// dispatch_group_notify(group, queue, ^{
// NSLog(@"完成、、、、、、");//所有group相關執行完的回調
// });
// NSLog(@".......完成、、、、、、");//這個會先於里面的打印
關於GCD信號量示例二:
/**
*
* 下面再展示一個信號量能實現的示例,而且這個示例是來自面試官的問題哦
* 問題需求:兩個異步任務嵌套,如何保證在內部的異步任務先執行?
*
* 本人分析:沒指明在什么隊列中取出任務,我先假設串行隊列。
* 如果是串行隊列的話,由於串行隊列FIFO,先創建的任務肯定先執行,執行完畢之后再執行下一個任務
* 如果是串行隊列中有兩個異步任務嵌套,那么外部的異步任務一定先執行,因為外部的任務先創建,先進隊列的,然后因為隊列FIFO
* 總結:如果要兩個異步任務嵌套,並且保證在內部的異步任務先執行,那么一定需要並發隊列。
*/
要解決面試的問題,本人立馬想到使用GCD的信號量的知識,下面是本人立馬通過代碼實現了解決了這個問題:
// 開始讓信號量為0,當信號量小於0的時候,當前線程會停下等待
dispatch_semaphore_t dispatchSemaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_queue_create("heyang", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
dispatch_async(queue, ^{
sleep(2);
NSLog(@"--1--");
NSLog(@"%@",[NSThread currentThread]);
// 發送一個信號量,就會讓那個信號量加1
dispatch_semaphore_signal(dispatchSemaphore);
});
// 信號量等待就會讓信號量減1
dispatch_semaphore_wait(dispatchSemaphore, DISPATCH_TIME_FOREVER);
NSLog(@"--2--");
NSLog(@"%@",[NSThread currentThread]);
});
如果前面理解了信號量,那么這段代碼就很好理解了。
關於信號量的使用易錯點補充:
下面是本人出錯的時候的代碼:
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
dispatch_async(queue, ^{
// +1
dispatch_semaphore_signal(semaphore);
NSLog(@"Hello - 2");
});
// 等待 -1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"Hello - 1");
});
}
錯誤的部分:紅色的代碼應該放在NSLog(@"Hello - 2")之后的,按照上面錯誤的邏輯就是這樣的:當代碼執行到紅色的代碼的時候,
因為信號量是全局管理所有線程的,那么紅色代碼就是發送信號量+1,這樣型號兩綜合就不等於-1了,就這樣所有線程都開通不停滯了。
接着上面代碼的兩個任務"NSLog(@"Hello - 2");"和"NSLog(@"Hello - 1");"又開始進入並發執行了,所以又進入隨機的執行
順序了。
為了保證內部的線程任務先執行,就必須把紅色代碼,也就是發送信號量的代碼放在當前任務函數的最后一行,保證前面的任務先執行。
6、GCD的計時器
代碼示例:
計時器事件稍有不同。它們不使用handle/mask參數,計時器事件使用另外一個函數 dispatch_source_set_timer
來配置計時器。這個函數使用三個參數來控制計時器觸發:
start
參數控制計時器第一次觸發的時刻。參數類型是 dispatch_time_t
,這是一個opaque類型,我們不能直接操作它。我們得需要 dispatch_time
和 dispatch_walltime
函數來創建它們。另外,常量 DISPATCH_TIME_NOW
和 DISPATCH_TIME_FOREVER
通常很有用。
interval(n. 間隔;間距;幕間休息)
參數沒什么好解釋的。
leeway
參 數比較有意思。這個參數告訴系統我們需要計時器觸發的精准程度。所有的計時器都不會保證100%精准,這個參數用來告訴系統你希望系統保證精准的努力程 度。如果你希望一個計時器沒五秒觸發一次,並且越准越好,那么你傳遞0為參數。另外,如果是一個周期性任務,比如檢查email,那么你會希望每十分鍾檢 查一次,但是不用那么精准。所以你可以傳入60,告訴系統60秒的誤差是可接受的。
這樣有什么意義呢?簡單來說,就是降低資源消耗。如果系 統可以讓cpu休息足夠長的時間,並在每次醒來的時候執行一個任務集合,而不是不斷的醒來睡去以執行任務,那么系統會更高效。如果傳入一個比較大的 leeway給你的計時器,意味着你允許系統拖延你的計時器來將計時器任務與其他任務聯合起來一起執行。
轉載本文注明出處:http://www.cnblogs.com/goodboy-heyang/p/5271513.html
附錄:很好的GCD學習的網頁鏈接
1、GCD這塊已經開源,地址http://libdispatch.macosforge.org
2、唐巧的技術博客:《使用GCD》,地址:http://blog.devtang.com/2012/02/22/use-gcd/
3、大神翻譯自國外IOS很不錯的學習網站文章《Grand Central Dispatch In-Depth: Part 1/2》:https://github.com/nixzhu/dev-blog
4、標哥的技術博客:《GCD由淺入深學習》:http://www.henishuo.com/gcd-multiple-thread-learn/
5、土土哥的《GCD使用經驗與技巧淺談》:http://tutuge.me/2015/04/03/something-about-gcd/
6、YouXianMing老師的《Grand Central Dispatch (GCD) Reference》:http://www.cnblogs.com/YouXianMing/p/3600763.html
7、《關於IOS多線程,你看我就夠了》:http://www.jianshu.com/p/0b0d9b1f1f19
8、《細說GCD(Grand Central Dispatch)如何使用》 :http://www.jianshu.com/p/fbe6a654604c
9、《【iOS】GCD死鎖》;http://www.brighttj.com/ios/ios-gcd-deadlock.html
10、很高大上的一個牛逼網站:http://www.dreamingwish.com/article/gcdgrand-central-dispatch-jiao-cheng.html