iOS多線程--下(GCD)

來源:互聯網
上載者:User

標籤:

1 GCD

它是一種純C語言,它是為多核並列運算設計的。可以自動管理線程的生命週期。
GCD 是面向任務和隊列的,不是面向線程的。他有兩個關鍵字“任務”“隊列”。
使用 GCD 的步驟主要是:
1 定製任務
2 任務添加到隊列中,隊列支援 FIFO 原則

#基本形式如下
dispath_queue_t queue = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFULT,0);dispath_async(queue,^{    //任務程式碼片段});
1.1 任務1.1.1 同步方式

在當前線程中執行,不具備開啟線程的能力。

dispath_sync( queue,block); //在隊列中執行 block 所定義的任務,以同步的方式
1.1.2 非同步方式

在新線程中執行,具備開啟新線程的能力

dispath_async(queue,block); //在隊列中執行 block 所定義的任務,以非同步方式

決定了是否有能力開啟新的線程

1.2 隊列1.2.1 並行隊列

並行隊列,隊列中的任務,以並行的方式進行,多任務同時進行。
一般情況下,我們使用的是全域的並發隊列。擷取全域並發隊列的方式如下:

//預設的寫法,擷取全域並發隊列。前一個參數是優先順序預設,後面一個參數是蘋果保留的參數dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
1.2.2 串列隊列

串列隊列中,隊列中的人物,都是以串列的方式進行。先執行一個任務,再執行另一個任務。
串列隊列分為兩種,一種是手動建立的串列隊列,另一個是主隊列

1.2.2.1 手動建立隊列
//預設寫法,第一個參數是隊列名稱,隨便填寫;第二個參數是隊列屬性,一般情況下寫 NULLdispatch_queue_create("queueName",NULL);
1.2.2.2 主隊列
//預設寫法dispath_get_main_queue();主隊列的任務執行,都是在主線程中進行的,一般用來做線程間的通訊。
2 常見的組合方式2.1 非同步方式+並發隊列(最常用)

①會建立新的線程
②並發執行任務
下面的例子中,任務1和任務2是並發執行的

//執行個體代碼//①擷取全域並發隊列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);//②非同步方式處理任務1dispatch_async(queue,^{    NSLog("下載圖片1--%@",[NSThread currentThread]);});//②非同步方式處理任務2dispatch_async(queue,^{    NSLog("下載圖片2--%@",[NSThread currentThread]);});
2.2 非同步方式+串列隊列

①會建立新線程,因為是串列方式執行,一般情況下只會建立一條線程。
②隊列中的任務以串列方式執行:任務1和任務2是串列執行的

//範例程式碼//①手動建立串列隊列dispatch_queue_t queue = dispatch_queue_create("queueName",NULL);//②非同步方式處理任務1dispatch_async(queue,^{    NSLog("正在下載圖片1---%@",[NSThread currentThread]);});//②非同步方式處理任務2dispatch_async(queue,^{    NSLog("正在下載圖片2---%@",[NSThread currentThread]);});
2.3 同步方式+並發隊列

①不會建立線程
②並發隊列的並發功能消失,隊列中的所有任務串列執行。

//範例程式碼//①擷取全域並發隊列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);//②同步執行任務1dispatch_sync(queue,^{    NSLog(@"下載圖片1---%@",[NSThread currentThread]);});//②同步執行任務2dispatch_sync(queue,^{    NSLog(@"下載圖片2---%@",[NSThread currentThread]);});
2.4 同步方式+串列隊列

①不會建立新線程
②串列隊列中的所有任務串列執行

//範例程式碼//①手動建立串列隊列dispatch_queue_t queue = dispatch_queue_create("queueName",NULL);//②同步執行任務1dispatch_sync(queue,^{    NSLog(@"正在下載圖片1-----%@",[]NSThread currentThread]);});//②同步執行任務2dispatch_sync(queue,^{    NSLog(@"正在下載圖片2-----%@",[]NSThread currentThread]);});
2.5 主隊列+非同步方式(主隊列也是串列隊列)

①主隊列是特殊的隊列,此時非同步方式雖然具備建立新線程的能力,但是實際上並不能建立新線程
②所有的任務都是在主隊列中串列執行的,也就是在主線程中進行,一般用來做進程之間的通訊。

//①擷取當前的主隊列dispatch_queue_t queue = dispatch_get_main_queue();//②非同步方式執行dispatch_async(queue,^{    NSLog(@"正在下載圖片---%@",[NSThread currentThread]);});
2.6 主隊列+同步方式(主隊列也是串列隊列)
  • 這種方式,會卡死整個程式,不用
  • 當前執行下面的程式是在主線程中執行的,當執行到第二步的時候,需要將下面的任務加入到主隊列中串列執行。因為是串列執行,主線程必須等到第二步結束後才能繼續向下執行,但是第二步是將任務載入到了主隊列的末尾,必須要等到之前主線程隊列執行完畢後才能執行,所以陷入了卡死狀態。

    //1擷取主隊列dispatch_queue_t queue = dispatch_get_main_queue();//2同步執行主隊列dispatch_sync(queue,^{    NSLog(@"正在執行。。。");});
3 隊列的記憶體管理

需要遵循的原則:
* 凡是函數名中帶有 create\copy\new\retain 等字眼,都應該在不需要使用這個資料的時候release 操作
* GCD的資料類型,在ARC下不需要再 release
* CF(Core Foundation)的資料類型在 ARC 和 MRC 環境下都需要手動 release。
例子:
* CFRelease(id);

NSDictionary *dict = @{@"1":@"1"};CFDictionaryRef dictCF = (__bridge CFDictionaryRef)(dict);CFRelease(dictCF);
4 線程之間的通訊

舉例,點擊控制器的 View,在子線程中從網上下載一張圖片,下載完畢後在主線程中更新按鈕的圖片。

//1 擷取全域並發隊列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//2 非同步方式下載圖片dispatch_async(queue, ^{    NSString *str = @"http://u1.img.mobile.sina.cn/public/files/image/600x150_img577f313ec621a.png";    NSURL *url = [NSURL URLWithString:str];    NSData  *data = [NSData dataWithContentsOfURL:url];    UIImage *image = [UIImage imageWithData:data];    //3 回到主線程,給 UIButton 設定圖片    dispatch_async(dispatch_get_main_queue(), ^{        [self.button setImage:image forState:UIControlStateNormal];    });});
5 延時執行操作

在程式設計中,延時執行的方式有三種

5.1 sleep

想要做延時操作,此種方式只需要在延時執行的操作之前執行如下代碼即可

//使當前線程睡眠3秒鐘後再次執行[NSThread sleepForTimeInterval:3]

缺點:會卡死當前調用的線程,整個線程會停滯。如果卡死的是主線程,那麼意味著 UI 會受很大的影響。

5.2 performSelectorAfter
//當3秒後,執行 download 方法。3秒後在主線程中執行該操作[self performSelector:@selector(download) withObject:nil afterDelay:3]
5.3 GCD

可以根據隊列的類型,決定延時後的操作在哪個線程中執行,可以指定在全域並發隊列中執行,也可以指定在主隊列中執行。

//dispatch_queue_t queue = dispatch_get_main_queue();dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{    NSLog(@"延時操作---%@",[NSThread currentThread]);});
6 一次性代碼

某個程式碼片段,在程式運行過程中,不管調用多少次,實際上改代碼只執行了一次,這就是一次性代碼
比如說:點擊介面後,開始下載圖片。如果下載圖片的代碼不是一次性代碼的話,每點擊一次介面,都需要再次下載,顯然不符合實際情況。使用其他方式也可以實現(定義變數或者定義標識),但一次性代碼是最簡單的操作。

static dispatch_once_t oneceToken;dispatch_once(&onceToken,^{    //想要只執行一次的代碼    //.....});
7 隊列組

隊列組就是一個對象,內部包含了隊列,當隊列中的任務執行完畢後,會自動調用響應的方法,這就是隊列組。

需求:
需要從網路上下載一張圖片,然後再下載一張 logo 圖片,用 logo 做浮水印。如果按照順序,先下載圖片,再下載 logo,最後進行圖片浮水印疊加,會比較耗時。最好的辦法是 兩張圖片分別放到兩個線程中進行,當兩個圖片全部下載好之後,再進行組裝。               
實現思路:
採用隊列組,手動建立一個隊列組,使用dispatch_group_async(group,queue,^{})來開闢新線程執行下載操作。當隊列組中的隊列任務全部結束之後,會自動調用 dispatch_group_notify(queue,^{})函數,也就是說我們可以將合并浮水印的操作放在這個函數中進行。
範例程式碼
//1 擷取全域並發隊列dispatch_queue_t queue  = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY——DEFAULT,0);//2 建立隊列組--如果是 MRC 環境中,需要 release操作: dispatch_release(group);dispatch_group_t group = dispatch_group_create(); //3 下載圖片1__block UIImage *image1 = nil; //加__block 修飾是為了 block 中可以訪問該變數dispatch_group_async(group,queue,^{    NSString *str = @"http://image1.png";    NSURL *url = [NSURL urlWithString:str];    NSData *data = [NSData dataWithContentsOfURL:url];    image1 = [UIImage imageWithData:data];});//4 下載圖片2__block UIImage *image2 = nil; //加__block 修飾是為了 block 中可以訪問該變數dispatch_group_async(group,queue,^{    NSString *str = @"http://image2.png";    NSURL *url = [NSURL urlWithString:str];    NSData *data = [NSData dataWithContentsOfURL:url];    image2 = [UIImage imageWithData:data];});//5 合并圖片--group中所有隊列的任務執行完之後,自動調用下面的函數dispatch_group_notify(group,queue,^{    //5.1 開啟當前圖形上下文    UIGraphicsBeginImageContextWithOptions(image1.size,NO,0.0);    //5.2 將 image1畫在上下文    [image1 drawInRect:CGRectMake(0,0,image1.size.width,image1..size.height)];    //5.3 將 image2畫在上下文    [image2 drawInRect:CGRectMake(0,0,100.50)];    //5.4 擷取當前內容相關的圖片    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();    //5.6 回到主線程,重新整理 UI    dispatch_async(dispath_get_main_queue,^{        self.imageView.image = image;    });});

iOS多線程--下(GCD)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.