IOS開發(fā)之多線程N(yùn)SThiread GCD NSOperation Runloop
IOS中的進(jìn)程和線程
通長來說一個(gè)app就是一個(gè)進(jìn)程
ios開發(fā)中較少的運(yùn)用進(jìn)程間的通信(XPC),絕大多數(shù)使用線程。
在ios開發(fā)中,為了保證流暢性以及線程安全,所有與UI相關(guān)的操作都應(yīng)該放在主線程,所以有時(shí)候主線程也叫UI線程。
影響UI體驗(yàn),耗時(shí)時(shí)間較長的操作,盡量放到非主線程中。比如網(wǎng)絡(luò)請(qǐng)求以及和本地的IO操作。
在IOS開發(fā)中有關(guān)于多線程的知識(shí)點(diǎn)主要包括:NSThread、GCD、NSOperation和Runloop
NSThread
NSthread就是一個(gè)線程,它的底層是對(duì)pthread的封裝,用于創(chuàng)建一個(gè)新的線程,我們也可以通過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;
比如我們?cè)谧泳€程中從網(wǎng)絡(luò)請(qǐng)求圖片(因?yàn)榫W(wǎng)絡(luò)請(qǐng)求比較耗時(shí)),并顯示在UI頁面中。
NSThread *downLoadImageThread = [[NSThread alloc] initWithBlock:^{ //將高耗時(shí)的獲取圖片的代碼放到子線程中執(zhí)行 UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:item.picUrl]]]; self.rightimageView.image = image; //UI操作必須在主線程中執(zhí)行,否則會(huì)報(bào)警 }]; //設(shè)置線程名字 downLoadImageThread.name = @"downLoadImageThread"; [downLoadImageThread start]; //執(zhí)行此線程
上面的代碼中,我們把UI顯示的操作放在了子線程中,這是不合規(guī)范的,因?yàn)樵趇os中,UI操作的代碼都必須放在主線程中,否則系統(tǒng)會(huì)報(bào)警。
GCD
GCD是代碼中dispatch開頭的相關(guān)的代碼。GCD解決了NSThread使用的不方便。它將對(duì)線程的操作變成了對(duì)隊(duì)列的操作。它簡化了我們對(duì)線程的管理,GCD在底層為我們實(shí)現(xiàn)了一個(gè)線程池自動(dòng)的管理線程,我們只要對(duì)隊(duì)列操作就可以了。和線程一樣,隊(duì)列也分主隊(duì)列和非主隊(duì)列,主隊(duì)列里存放的是主線程,非主隊(duì)列里存放的是非主線程。
如下圖:
GCD中主要有三種隊(duì)列:
第一:是主線程對(duì)應(yīng)的主隊(duì)列。
dispatch_queue_main_t mainQueue = dispatch_get_main_queue(); //獲取主隊(duì)列
第二:非主線程按照優(yōu)先級(jí)分為4中不同優(yōu)先級(jí)的非主隊(duì)列。High/default/Low/Background
其定義的函數(shù)如下,其中第一個(gè)參數(shù)是優(yōu)先級(jí)的選擇,第二個(gè)參數(shù)暫時(shí)用不到可以填0
dispatch_queue_global_t downoadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //獲取非主隊(duì)列
第三:自定義隊(duì)列
創(chuàng)建函數(shù)如下:第一個(gè)參數(shù)是設(shè)定隊(duì)列的名字,第二個(gè)參數(shù) 是設(shè)定隊(duì)列是串行的,還是并發(fā)的。至于串行隊(duì)列和并發(fā)隊(duì)列的概念,下面會(huì)仔細(xì)分析。
串行:DISPATCH_QUEUE_SERIAL
并行:DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t dispatch_queue_create(const char *_Nullable label, dispatch_queue_attr_t _Nullable attr);
GCD的使用,分為同步執(zhí)行和異步執(zhí)行。
同步執(zhí)行,也就是代碼一行一行的執(zhí)行。其函數(shù)調(diào)用如下:
dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
其中第一個(gè)參數(shù)的隊(duì)列名,第二個(gè)參數(shù)是代碼塊,代碼塊中是我們要在該線程中執(zhí)行的代碼。
異步執(zhí)行,就是代碼可以跳出當(dāng)前代碼塊,執(zhí)行當(dāng)前代碼之后的代碼,其函數(shù)調(diào)用如下:
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
其中第一個(gè)參數(shù)是隊(duì)列名,第二個(gè)參數(shù)是要執(zhí)行的代碼塊。
還有一種執(zhí)行方式,是延遲執(zhí)行,其函數(shù)調(diào)用如下:
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
第一個(gè)參數(shù)是延遲時(shí)間,后面參數(shù)同上。
概念解讀:
同步執(zhí)行:只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力。
異步執(zhí)行:異步添加任務(wù)到指定的隊(duì)列中,它不會(huì)做任何等待,可以繼續(xù)執(zhí)行任務(wù)??梢栽谛碌木€程中執(zhí)行任務(wù),具備開啟(創(chuàng)建)新線程的能力。
串行隊(duì)列:每次只有一個(gè)任務(wù)被執(zhí)行,讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行。只開啟一個(gè)線程,一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù)。
并行隊(duì)列:可以讓多個(gè)任務(wù)并打(同時(shí))執(zhí)行。(可以開啟多個(gè)線程,并且同時(shí)執(zhí)行任務(wù))
注意:并行隊(duì)列的并發(fā)功能只有在異步方法下才有效。
這里,我舉一個(gè)例子,來解釋一下串行隊(duì)列和并發(fā)隊(duì)列的區(qū)別,以及同步執(zhí)行和異步執(zhí)行的區(qū)別。
假設(shè)現(xiàn)在有 5 個(gè)人要穿過一道門禁,這道門禁總共有 10 個(gè)入口,管理員可以決定同一時(shí)間打開幾個(gè)入口,可以決定同一時(shí)間讓一個(gè)人單獨(dú)通過還是多個(gè)人一起通過。不過默認(rèn)情況下,管理員只開啟一個(gè)入口,且一個(gè)通道一次只能通過一個(gè)人。
這個(gè)故事里,人好比是 任務(wù),管理員好比是 系統(tǒng),入口則代表 線程。
5個(gè)人表示有 5 個(gè)任務(wù),10 個(gè)入口代表 10 條線程。
串行隊(duì)列 好比是 5 個(gè)人排成一支長隊(duì)。
并發(fā)隊(duì)列 好比是 5 個(gè)人排成多支隊(duì)伍,比如 2 隊(duì),或者 3 隊(duì)。
同步任務(wù) 好比是管理員只開啟了一個(gè)入口(當(dāng)前線程)。
異步任務(wù) 好比是管理員同時(shí)開啟了多個(gè)入口(當(dāng)前線程 + 新開的線程)。
『異步執(zhí)行 + 并發(fā)隊(duì)列』 可以理解為:現(xiàn)在管理員開啟了多個(gè)入口(比如 3 個(gè)入口),5 個(gè)人排成了多支隊(duì)伍(比如 3 支隊(duì)伍),這樣這 5 個(gè)人就可以 3 個(gè)人同時(shí)一起穿過門禁了。
『同步執(zhí)行 + 并發(fā)隊(duì)列』 可以理解為:現(xiàn)在管理員只開啟了 1 個(gè)入口,5 個(gè)人排成了多支隊(duì)伍。雖然這 5 個(gè)人排成了多支隊(duì)伍,但是只開了 1 個(gè)入口啊,這 5 個(gè)人雖然都想快點(diǎn)過去,但是 1 個(gè)入口一次只能過 1 個(gè)人,所以大家就只好一個(gè)接一個(gè)走過去了,表現(xiàn)的結(jié)果就是:順次通過入口。
換成 GCD 里的語言就是說:
『異步執(zhí)行 + 并發(fā)隊(duì)列』就是:系統(tǒng)開啟了多個(gè)線程(主線程+其他子線程),任務(wù)可以多個(gè)同時(shí)運(yùn)行。
『同步執(zhí)行 + 并發(fā)隊(duì)列』就是:系統(tǒng)只默認(rèn)開啟了一個(gè)主線程,沒有開啟子線程,雖然任務(wù)處于并發(fā)隊(duì)列中,但也只能一個(gè)接一個(gè)執(zhí)行了。
下面我用GCD來優(yōu)化一下上面UI操作在子線程中執(zhí)行的警告問題:
//方法三:使用GCD實(shí)現(xiàn) dispatch_queue_global_t downoadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //獲取非主隊(duì)列 dispatch_queue_main_t mainQueue = dispatch_get_main_queue(); //獲取主隊(duì)列 //在非主隊(duì)列中,異步執(zhí)行,執(zhí)行獲取圖片的高耗時(shí)操作 //異步:打開了多道門,可以同時(shí)穿過門(多線程)具備開啟新線程的能力 dispatch_async(downoadQueue, ^{ //異步 ,非主隊(duì)列 UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:item.picUrl]]]; dispatch_async(mainQueue, ^{ //異步,主線程中執(zhí)行UI操作 //在主線程中執(zhí)行UI操作,異步執(zhí)行 self.rightimageView.image = image; //UI操作必須在主線程中執(zhí)行,否則會(huì)報(bào)警 }); });
NSOperation
由于GCD并沒有針對(duì)面向?qū)ο蟮姆庋b,而我們要執(zhí)行的代碼都是寫在block中的,如果我們要取消執(zhí)行一般的block,或者要在多個(gè)block之間實(shí)現(xiàn)同步和互斥,操作起來都比較復(fù)雜。
對(duì)于更上層的封裝,系統(tǒng)為我們提供了NSOperation,它是系統(tǒng)對(duì)GCD的一個(gè)面向?qū)ο蟮姆庋b。
關(guān)于NSOperation,我后面深入了解后,再進(jìn)行分享吧。
Runloop
配合著線程是如何進(jìn)行業(yè)務(wù)邏輯的操作,以及業(yè)務(wù)邏輯的執(zhí)行的,對(duì)于每一個(gè)線程,系統(tǒng)都提供了一個(gè)內(nèi)部實(shí)現(xiàn),這個(gè)內(nèi)部實(shí)現(xiàn)就是Runloop。Runloop就是配合著底層的thread,來處理我們的手勢,交互,以及一些端口的管理等。
舉個(gè)例子,主線程為什么會(huì)一直存在不被銷毀,底層就是Runloop在維護(hù),它讓主線程在不執(zhí)行的時(shí)候進(jìn)行睡眠。
最后,我們從宏觀對(duì)IOS的多線程有一個(gè)認(rèn)識(shí)。
以上就是IOS開發(fā)之多線程N(yùn)SThiread GCD NSOperation Runloop的詳細(xì)內(nèi)容,更多關(guān)于IOS開發(fā)之多線程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
iOS10最新實(shí)現(xiàn)遠(yuǎn)程通知的開發(fā)教程詳解
這篇文章主要介紹了iOS10最新遠(yuǎn)程通知開發(fā)的實(shí)現(xiàn)過程,文章先對(duì)推送通知以及遠(yuǎn)程推送通知等進(jìn)行了基本介紹,然后通過示例代碼詳細(xì)介紹了iOS10 全新遠(yuǎn)程通知的教程,有需要的朋友們可以參考借鑒,下面來一起看看吧。2016-09-09詳解iOS中position:fixed吸底時(shí)的滑動(dòng)出現(xiàn)抖動(dòng)的解決方案
這篇文章主要介紹了詳解iOS中position:fixed吸底時(shí)的滑動(dòng)出現(xiàn)抖動(dòng)的解決方案,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12iOS應(yīng)用運(yùn)用設(shè)計(jì)模式中的Strategy策略模式的開發(fā)實(shí)例
這篇文章主要介紹了iOS應(yīng)用開發(fā)中對(duì)設(shè)計(jì)模式中的Strategy策略模式的運(yùn)用,例子采用傳統(tǒng)的Objective-C語言代碼演示,需要的朋友可以參考下2016-03-03iOS開發(fā)中UIImageView控件的常用操作整理
這篇文章主要介紹了iOS開發(fā)中UIImageView控件的常用操作整理,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2016-01-01總結(jié)IOS關(guān)閉鍵盤/退出鍵盤的五種方式
IOS開發(fā)中經(jīng)常要用到輸入框,默認(rèn)情況下點(diǎn)擊輸入框就會(huì)彈出鍵盤,但是必須要實(shí)現(xiàn)輸入框return的委托方法才能取消鍵盤的顯示,對(duì)于用戶體驗(yàn)來說很不友好,我們可以實(shí)現(xiàn)例如點(diǎn)擊鍵盤以外的空白區(qū)域來將鍵盤關(guān)閉的功能,以下是我總結(jié)出的幾種關(guān)閉鍵盤的方法。2016-08-08IOS 屏幕適配方案實(shí)現(xiàn)縮放window的示例代碼
這篇文章主要介紹了IOS 屏幕適配方案實(shí)現(xiàn)縮放window的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04在IOS中為什么使用多線程及多線程實(shí)現(xiàn)的三種方法
這篇文章給大家介紹在IOS中為什么使用多線程及多線程實(shí)現(xiàn)的三種方法,基本上使用這三種方法實(shí)現(xiàn)多線程(NSThread Grand Centeral Dispatch(GCD) NSOperation和NSOperationQueue),感興趣的朋友可以參考下本篇文章2015-11-11iOS中的導(dǎo)航欄UINavigationBar與工具欄UIToolBar要點(diǎn)解析
UINavigation可以附著于導(dǎo)航控制器之中使用,也可以在controller中單獨(dú)使用,這里我們將來看iOS中的導(dǎo)航欄UINavigationBar與工具欄UIToolBar要點(diǎn)解析.2016-06-06