IOS開發之多線程NSThiread GCD NSOperation Runloop

IOS中的進程和線程

通長來說一個app就是一個進程
ios開發中較少的運用進程間的通信(XPC),絕大多數使用線程。
在ios開發中,為瞭保證流暢性以及線程安全,所有與UI相關的操作都應該放在主線程,所以有時候主線程也叫UI線程。
影響UI體驗,耗時時間較長的操作,盡量放到非主線程中。比如網絡請求以及和本地的IO操作。
在IOS開發中有關於多線程的知識點主要包括:NSThread、GCD、NSOperation和Runloop

NSThread

NSthread就是一個線程,它的底層是對pthread的封裝,用於創建一個新的線程,我們也可以通過NSThread中的一些屬性來獲取信息,比如currentThread,isMainThread。

@property (readonly) BOOL isMainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@property (class, readonly, strong) NSThread *currentThread;

比如我們在子線程中從網絡請求圖片(因為網絡請求比較耗時),並顯示在UI頁面中。

NSThread *downLoadImageThread = [[NSThread alloc] initWithBlock:^{
    //將高耗時的獲取圖片的代碼放到子線程中執行
    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:item.picUrl]]];
    self.rightimageView.image = image;  //UI操作必須在主線程中執行,否則會報警
}];
//設置線程名字
downLoadImageThread.name = @"downLoadImageThread";
[downLoadImageThread start]; //執行此線程

上面的代碼中,我們把UI顯示的操作放在瞭子線程中,這是不合規范的,因為在ios中,UI操作的代碼都必須放在主線程中,否則系統會報警。

報警如圖:

GCD

GCD是代碼中dispatch開頭的相關的代碼。GCD解決瞭NSThread使用的不方便。它將對線程的操作變成瞭對隊列的操作。它簡化瞭我們對線程的管理,GCD在底層為我們實現瞭一個線程池自動的管理線程,我們隻要對隊列操作就可以瞭。和線程一樣,隊列也分主隊列和非主隊列,主隊列裡存放的是主線程,非主隊列裡存放的是非主線程。
如下圖:

GCD中主要有三種隊列:
第一:是主線程對應的主隊列。

dispatch_queue_main_t mainQueue = dispatch_get_main_queue(); //獲取主隊列

第二:非主線程按照優先級分為4中不同優先級的非主隊列。High/default/Low/Background
其定義的函數如下,其中第一個參數是優先級的選擇,第二個參數暫時用不到可以填0

dispatch_queue_global_t downoadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //獲取非主隊列

第三:自定義隊列
創建函數如下:第一個參數是設定隊列的名字,第二個參數 是設定隊列是串行的,還是並發的。至於串行隊列和並發隊列的概念,下面會仔細分析。
串行:DISPATCH_QUEUE_SERIAL
並行:DISPATCH_QUEUE_CONCURRENT

dispatch_queue_t dispatch_queue_create(const char *_Nullable label,
		dispatch_queue_attr_t _Nullable attr);

GCD的使用,分為同步執行和異步執行。
同步執行,也就是代碼一行一行的執行。其函數調用如下:

dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);

其中第一個參數的隊列名,第二個參數是代碼塊,代碼塊中是我們要在該線程中執行的代碼
異步執行,就是代碼可以跳出當前代碼塊,執行當前代碼之後的代碼,其函數調用如下:

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

其中第一個參數是隊列名,第二個參數是要執行的代碼塊。
還有一種執行方式,是延遲執行,其函數調用如下:

void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
		dispatch_block_t block);

第一個參數是延遲時間,後面參數同上。
概念解讀
同步執行:隻能在當前線程中執行任務,不具備開啟新線程的能力。
異步執行:異步添加任務到指定的隊列中,它不會做任何等待,可以繼續執行任務。可以在新的線程中執行任務,具備開啟(創建)新線程的能力。
串行隊列:每次隻有一個任務被執行,讓任務一個接著一個地執行。隻開啟一個線程,一個任務執行完畢後,再執行下一個任務。
並行隊列:可以讓多個任務並打(同時)執行。(可以開啟多個線程,並且同時執行任務)
註意:並行隊列的並發功能隻有在異步方法下才有效。

這裡,我舉一個例子,來解釋一下串行隊列和並發隊列的區別,以及同步執行和異步執行的區別。
假設現在有 5 個人要穿過一道門禁,這道門禁總共有 10 個入口,管理員可以決定同一時間打開幾個入口,可以決定同一時間讓一個人單獨通過還是多個人一起通過。不過默認情況下,管理員隻開啟一個入口,且一個通道一次隻能通過一個人。
這個故事裡,人好比是 任務,管理員好比是 系統,入口則代表 線程。
5個人表示有 5 個任務,10 個入口代表 10 條線程。
串行隊列 好比是 5 個人排成一支長隊。
並發隊列 好比是 5 個人排成多支隊伍,比如 2 隊,或者 3 隊。
同步任務 好比是管理員隻開啟瞭一個入口(當前線程)。
異步任務 好比是管理員同時開啟瞭多個入口(當前線程 + 新開的線程)。
『異步執行 + 並發隊列』 可以理解為:現在管理員開啟瞭多個入口(比如 3 個入口),5 個人排成瞭多支隊伍(比如 3 支隊伍),這樣這 5 個人就可以 3 個人同時一起穿過門禁瞭。
『同步執行 + 並發隊列』 可以理解為:現在管理員隻開啟瞭 1 個入口,5 個人排成瞭多支隊伍。雖然這 5 個人排成瞭多支隊伍,但是隻開瞭 1 個入口啊,這 5 個人雖然都想快點過去,但是 1 個入口一次隻能過 1 個人,所以大傢就隻好一個接一個走過去瞭,表現的結果就是:順次通過入口。
換成 GCD 裡的語言就是說:
『異步執行 + 並發隊列』就是:系統開啟瞭多個線程(主線程+其他子線程),任務可以多個同時運行。
『同步執行 + 並發隊列』就是:系統隻默認開啟瞭一個主線程,沒有開啟子線程,雖然任務處於並發隊列中,但也隻能一個接一個執行瞭。
下面我用GCD來優化一下上面UI操作在子線程中執行的警告問題:

//方法三:使用GCD實現
    dispatch_queue_global_t downoadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //獲取非主隊列
    dispatch_queue_main_t mainQueue = dispatch_get_main_queue(); //獲取主隊列
    //在非主隊列中,異步執行,執行獲取圖片的高耗時操作
    //異步:打開瞭多道門,可以同時穿過門(多線程)具備開啟新線程的能力
    dispatch_async(downoadQueue, ^{  //異步 ,非主隊列
        UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:item.picUrl]]];
        dispatch_async(mainQueue, ^{  //異步,主線程中執行UI操作
            //在主線程中執行UI操作,異步執行
            self.rightimageView.image = image;  //UI操作必須在主線程中執行,否則會報警
        });
    });

NSOperation

由於GCD並沒有針對面向對象的封裝,而我們要執行的代碼都是寫在block中的,如果我們要取消執行一般的block,或者要在多個block之間實現同步和互斥,操作起來都比較復雜。
對於更上層的封裝,系統為我們提供瞭NSOperation,它是系統對GCD的一個面向對象的封裝。
關於NSOperation,我後面深入瞭解後,再進行分享吧。

Runloop

配合著線程是如何進行業務邏輯的操作,以及業務邏輯的執行的,對於每一個線程,系統都提供瞭一個內部實現,這個內部實現就是Runloop。Runloop就是配合著底層的thread,來處理我們的手勢,交互,以及一些端口的管理等。

舉個例子,主線程為什麼會一直存在不被銷毀,底層就是Runloop在維護,它讓主線程在不執行的時候進行睡眠。

最後,我們從宏觀對IOS的多線程有一個認識。

以上就是IOS開發之多線程NSThiread GCD NSOperation Runloop的詳細內容,更多關於IOS開發之多線程的資料請關註WalkonNet其它相關文章!

推薦閱讀:

    None Found