你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS多線程--下(GCD)

iOS多線程--下(GCD)

編輯:IOS開發綜合

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 手動創建隊列
//默認寫法,第一個參數是隊列名稱,隨便填寫;第二個參數是隊列屬性,一般情況下寫 NULL
dispatch_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);
//②異步方式處理任務1
dispatch_async(queue,^{
    NSLog("下載圖片1--%@",[NSThread currentThread]);
});
//②異步方式處理任務2
dispatch_async(queue,^{
    NSLog("下載圖片2--%@",[NSThread currentThread]);
});

2.2 異步方式+串行隊列

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

//示例代碼
//①手動創建串行隊列
dispatch_queue_t queue = dispatch_queue_create("queueName",NULL);
//②異步方式處理任務1
dispatch_async(queue,^{
    NSLog("正在下載圖片1---%@",[NSThread currentThread]);
});
//②異步方式處理任務2
dispatch_async(queue,^{
    NSLog("正在下載圖片2---%@",[NSThread currentThread]);
});

2.3 同步方式+並發隊列

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

//示例代碼
//①獲取全局並發隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
//②同步執行任務1
dispatch_sync(queue,^{
    NSLog(@"下載圖片1---%@",[NSThread currentThread]);
});
//②同步執行任務2
dispatch_sync(queue,^{
    NSLog(@"下載圖片2---%@",[NSThread currentThread]);
});

2.4 同步方式+串行隊列

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

//示例代碼
//①手動創建串行隊列
dispatch_queue_t queue = dispatch_queue_create("queueName",NULL);
//②同步執行任務1
dispatch_sync(queue,^{
    NSLog(@"正在下載圖片1-----%@",[]NSThread currentThread]);
});
//②同步執行任務2
dispatch_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;
    });
});
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved