1. 程式人生 > >GCD 的使用與理解(1)

GCD 的使用與理解(1)

什麼是GCD

Grand Central Dispatch (GCD)是Apple開發的一個多核程式設計的解決方法。該方法在Mac OS X 10.6雪豹中首次推出,並隨後被引入到了iOS4.0中。GCD是一個替代諸如NSThread, NSOperationQueue, NSInvocationOperation等技術的很高效和強大的技術。

GCD和block的配合使用,可以方便地進行多執行緒程式設計。

應用舉例

讓我們來看一個程式設計場景。我們要在iPhone上做一個下載網頁的功能,該功能非常簡單,就是在iPhone上放置一個按鈕,點選該按鈕時,顯示一個轉動的圓圈,表示正在進行下載,下載完成之後,將內容載入到介面上的一個文字控制元件中。

不用GCD前

雖然功能簡單,但是我們必須把下載過程放到後臺執行緒中,否則會阻塞UI執行緒顯示。所以,如果不用GCD, 我們需要寫如下3個方法:

  • someClick 方法是點選按鈕後的程式碼,可以看到我們用NSInvocationOperation建了一個後臺執行緒,並且放到NSOperationQueue中。後臺執行緒執行download方法。
  • download 方法處理下載網頁的邏輯。下載完成後用performSelectorOnMainThread執行download_completed 方法。
  • download_completed 進行clear up的工作,並把下載的內容顯示到文字控制元件中。

這3個方法的程式碼如下。可以看到,雖然 開始下載 –> 下載中 –> 下載完成 這3個步驟是整個功能的三步。但是它們卻被切分成了3塊。他們之間因為是3個方法,所以還需要傳遞資料引數。如果是複雜的應用,資料引數很可能就不象本例子中的NSString那麼簡單了,另外,下載可能放到Model的類中來做,而介面的控制放到View Controller層來做,這使得本來就分開的程式碼變得更加散落。程式碼的可讀性大大降低。

static NSOperationQueue * queue;

- (IBAction)someClick:(id)sender {
    self.indicator.hidden = NO;
    [self.indicator startAnimating];
    queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation * op = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil] autorelease];
    [queue addOperation:op];
}

- (void)download {
    NSURL * url = [NSURL URLWithString:@"http://www.youdao.com"];
    NSError * error;
    NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
    if (data != nil) {
        [self performSelectorOnMainThread:@selector(download_completed:) withObject:data waitUntilDone:NO];
    } else {
        NSLog(@"error when download:%@", error);
        [queue release];
    }
}

- (void) download_completed:(NSString *) data {
    NSLog(@"call back");
    [self.indicator stopAnimating];
    self.indicator.hidden = YES;
    self.content.text = data;
    [queue release];
}


使用GCD後

如果使用GCD,以上3個方法都可以放到一起,如下所示:

// 原始碼塊一
self.indicator.hidden = NO;
[self.indicator startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 原始碼塊二
    NSURL * url = [NSURL URLWithString:@"http://www.youdao.com"];
    NSError * error;
    NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
    if (data != nil) {
        // 原始碼塊三
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.indicator stopAnimating];
            self.indicator.hidden = YES;
            self.content.text = data;
        });
    } else {
        NSLog(@"error when download:%@", error);
    }
});

首先我們可以看到,程式碼變短了。因為少了原來3個方法的定義,也少了相互之間需要傳遞的變數的封裝。

另外,程式碼變清楚了,雖然是非同步的程式碼,但是它們被GCD合理的整合在一起,邏輯非常清晰。如果應用上MVC模式,我們也可以將View Controller層的回撥函式用GCD的方式傳遞給Modal層,這相比以前用@selector的方式,程式碼的邏輯關係會更加清楚。

block的定義

block的定義有點象函式指標,差別是用 ^ 替代了函式指標的 * 號,如下所示:

// 申明變數
 (void) (^loggerBlock)(void);
 // 定義

 loggerBlock = ^{
      NSLog(@"Hello world");
 };
 // 呼叫
 loggerBlock();


但是大多數時候,我們通常使用內聯的方式來定義block,即將它的程式塊寫在呼叫的函式裡面,例如這樣:

 dispatch_async(dispatch_get_global_queue(0, 0), ^{
      // something
 });


從上面大家可以看出,block有如下特點:

  1. 程式塊可以在程式碼中以內聯的方式來定義。
  2. 程式塊可以訪問在建立它的範圍內的可用的變數。

系統提供的dispatch方法

為了方便地使用GCD,蘋果提供了一些方法方便我們將block放在主執行緒 或 後臺執行緒執行,或者延後執行。使用的例子如下:

 //  後臺執行:
 dispatch_async(dispatch_get_global_queue(0, 0), ^{
      // something
 });
 // 主執行緒執行:
 dispatch_async(dispatch_get_main_queue(), ^{
      // something
 });
 // 一次性執行:
 static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
     // code to be executed once
 });
 // 延遲2秒執行:
 double delayInSeconds = 2.0;
 dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
 dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
     // code to be executed on the main queue after delay
 });


dispatch_queue_t 也可以自己定義,如要要自定義queue,可以用dispatch_queue_create方法,示例如下:

dispatch_queue_t urls_queue = dispatch_queue_create("blog.devtang.com", NULL);
dispatch_async(urls_queue, ^{
     // your code
});
dispatch_release(urls_queue);


另外,GCD還有一些高階用法,例如讓後臺2個執行緒並行執行,然後等2個執行緒都結束後,再彙總執行結果。這個可以用dispatch_group, dispatch_group_async 和 dispatch_group_notify來實現,示例如下:

 dispatch_group_t group = dispatch_group_create();
 dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
      // 並行執行的執行緒一
 });
 dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
      // 並行執行的執行緒二
 });
 dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
      // 彙總結果
 });


修改block之外的變數

預設情況下,在程式塊中訪問的外部變數是複製過去的,即寫操作不對原變數生效。但是你可以加上 __block來讓其寫操作生效,示例程式碼如下:

 __block int a = 0;
 void  (^foo)(void) = ^{
      a = 1;
 }
 foo();
 // 這裡,a的值被修改為1


後臺執行

使用block的另一個用處是可以讓程式在後臺較長久的執行。在以前,當app被按home鍵退出後,app僅有最多5秒鐘的時候做一些儲存或清理資源的工作。但是應用可以呼叫UIApplication的beginBackgroundTaskWithExpirationHandler方法,讓app最多有10分鐘的時間在後臺長久執行。這個時間可以用來做清理本地快取,傳送統計資料等工作。

讓程式在後臺長久執行的示例程式碼如下:

// AppDelegate.h檔案
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask;

// AppDelegate.m檔案
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [self beingBackgroundUpdateTask];
    // 在這裡加上你需要長久執行的程式碼
    [self endBackgroundUpdateTask];
}

- (void)beingBackgroundUpdateTask
{
    self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [self endBackgroundUpdateTask];
    }];
}

- (void)endBackgroundUpdateTask
{
    [[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
    self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}


總結

總體來說,GCD能夠極大地方便開發者進行多執行緒程式設計。大家應該儘量使用GCD來處理後臺執行緒和UI執行緒的互動。

相關推薦

GCD 的使用理解1

什麼是GCD Grand Central Dispatch (GCD)是Apple開發的一個多核程式設計的解決方法。該方法在Mac OS X 10.6雪豹中首次推出,並隨後被引入到了iOS4.0中。GCD是一個替代諸如NSThread, NSOperationQueue, NSInvocationOpera

dubbo原始碼理解1啟動初始化bean載入

今天看了一些博文,都是關於dubbo原始碼解析方面的。覺得有必要記一下。 問題1:spring 如何注入dubbo 的?或者說怎麼整合dubbo 的,或者說 dubbo啟動時怎麼啟動spring的? 1、首先想要實現 在spring 中 發揮某框架的功能,就必須將該框架注入到springBe

ELMo模型的理解實踐1

論文:2018 NAACL 《Deep Contextualized Word Representations》 一、優點 1.學習單詞的複雜特徵,包括語法、語義 2.學習在不同上下文下的一詞多義 二、模型 1.Bidirectional language models(BLM)

Faster rcnn代碼理解1

感覺 組織 等我 ont 包含 還要 定義 fig 訓練數據 這段時間看了不少論文,回頭看看,感覺還是有必要將Faster rcnn的源碼理解一下,畢竟後來很多方法都和它有相近之處,同時理解該框架也有助於以後自己修改和編寫自己的框架。好的開始吧~ 這裏我們跟著Faster

checkbox復選框的一些深入研究理解

ack 註意 聖誕 return tle script ++ doc div 原文鏈接:http://www.zhangxinxu.com/wordpress/?p=466 1 <!DOCTYPE html> 2 <html lang="en">

lixuxmint系統定制配置1-系統初始配置

visible 可能 white div 當前 圖片 num lines 引導 小書匠 Linux 經常安裝新的系統,每次安裝完都得去搜索一邊如何將系統部署為之前的環境,不僅耗費時間,還不一定能弄回之前的環境,現在把從裸機->到工作環境的系統定制及配置過程記錄下來

Chap03知識抽取挖掘1

eva image str spa 實體 方法 深度學習 ron int 大綱 1.知識抽取任務定義和相關比賽 2.面向結構化數據的知識抽取 3.面向半結構化數據的知識抽取 4.實踐展示:基於百科數據的知識抽取 競賽:MUC ACE KBP SemEval

JSP學習理解

parse runtime this java服務 b站 sps odin 作用域 地址欄 一.JSP的運用 1).WHYJSP是簡Servlet編寫的一種技術,它將Java代碼和HTML語句混合在同一個文件中編寫,只對網頁中的要動態產生的內容采用java代碼來編寫,

python socket 的理解1

網絡 完成 ron bsp 接收數據 技術分享 檢測 cnblogs 無數據 前言 socket的用法簡單,但裏面的概念有點模糊,記錄自己本人的一點理解。 socket層結構圖 註意,從此圖中看出socket處於tcp和應用層之間。那麽它代表啥意思呢?簡明的說,數

redis叢集分片1-redis伺服器叢集、客戶端分片 redis叢集分片1-redis伺服器叢集、客戶端分片

redis叢集與分片(1)-redis伺服器叢集、客戶端分片   下面是來自知乎大神的一段說明,個人覺得非常清晰,就收藏了。 為什麼叢集? 通常,為了提高網站響應速度,總是把熱點資料儲存在記憶體中而不是直接從後端 資料庫中

redis集群分片1-redis服務器集群、客戶端分片

服務器集群 包含 工作 direct 數據丟失 網站 這一 線性 取模 下面是來自知乎大神的一段說明,個人覺得非常清晰,就收藏了。 為什麽集群? 通常,為了提高網站響應速度,總是把熱點數據保存在內存中而不是直接從後端數據庫中讀取。Redis是一個很好的Cache工具

wifi驅動的理解1——驅動架構

轉載請註明出處:http://blog.csdn.net/Righthek 謝謝!          在分析WIFI驅動前,分享一下個人對Linux驅動的一些瞭解,其實縱觀Linux眾多的裝置驅動,幾乎都是以匯流排為

Linux核心設計實現1--核心開發的特點

1. 核心程式設計時既不能訪問C庫也不能訪問標準的C標頭檔案        其中的原因有很多種。其一,C標準庫的很多函式實現都是基於核心實現的,這核心編譯的時候都還沒有核心,所以就不存在這些函式,這個就是先有雞還是先有蛋這個悖論。其二,其主主要的的

正則爬蟲1

正則是什麼 正則是一門小型的程式語言,在python中正則被封裝成re模組。自己對正則的理解就是用來匹配字串中一些字元,正則使得匹配字串的匹配更加多樣。 三種匹配方法 re模組中匹配了三種方法(findall,match,search) s = "/home/kioskday

資料結構——排序查詢1——排序查詢簡介

排序與查詢 排序,是指將一系列無序的記錄,通過某種方式或者演算法,將其變為有序的過程。如果排出來的順序是由小到大排列,我們就稱這種排序叫升序排序。如果是由大到小,我們就稱為降序排序。例如有一組資料 : 開始時為: 2 4 7 1 9 升序排序: 1 2 4 7 9 降序排序: 9 7

淺談對Js面向物件的理解1

    面向物件的語言有一個標誌,那就是它們都有類的概念,通過類來建立任意多個具有相同屬性和方法的物件。它是一種程式開發的方法,它將物件作為程式的基本單元,將邏輯和資料封裝其中,以提高程式碼的靈活性、重用性和擴充套件性。物件是把資料及對資料的操作方法放在一起,作為一個相互依存的整體。簡單的

GCD 深入理解

GCD 深入理解(二) 本文是基於上一篇文章: GCD 深入理解(一) 的後續 如果你還沒看過上一篇,那趕緊去看看吧。 本文講解了GCD的幾個更優秀的功能,快來看看吧。 原文地址:GCD 深入理解(二) ______

GCD 深入理解

GCD 深入理解(一) 這篇文章是上一篇iOS 多執行緒和GCD(Grand Central Dispath) 教程 (一)的續篇 因為第一篇只是一個簡單的入門,來告訴大傢什麼是多執行緒和GCD,並沒有詳細解釋GCD的眾多用法。 因此才有這麼一篇,後面還會有一篇。

3D引擎資料結構glTF1:簡介

不是有句老話講“程式 = 演算法 + 資料結構”嘛,對於3D引擎來說也是這樣。學習和掌握3D引擎中的核心資料有哪些,它們直接的關係是怎樣等等問題,對於理解3D引擎的架構和圖形渲染關係都有著非常大的幫助。然而,現在的商業3D引擎非常複雜,想要通過學習其原始碼嘛非常困難,那麼你就這樣放棄了嗎

初學者對指標的理解1

[email protected][TOC](如何理解c語言中的指標 標題 初學者對指標的理解(1) 你好! 這是你第一次使用 Markdown編輯器 所展示的歡迎頁。很多功能還不太熟練 什麼是指標 明白資料在記憶體中的儲存 當你在程式中定義一個變