OC中的多線程
OC中多線程根據(jù)封裝程度可以分為三個(gè)層次:NSThread、GCD和NSOperation,另外由于OC兼容C語(yǔ)言,因此仍然可以使用C語(yǔ)言的POSIX接口來(lái)實(shí)現(xiàn)多線程婚脱,只需引入相應(yīng)的頭文件:#include <pthread.h>。
NSThread
NSThread是封裝程度最小最輕量級(jí)的,使用更靈活障贸,但要手動(dòng)管理線程的生命周期荠割、線程同步和線程加鎖等粱锐,開(kāi)銷較大哆致;
NSThread的基本使用比較簡(jiǎn)單删掀,可以動(dòng)態(tài)創(chuàng)建初始化NSThread對(duì)象,對(duì)其進(jìn)行設(shè)置然后啟動(dòng)袁波;也可以通過(guò)NSThread的靜態(tài)方法快速創(chuàng)建并啟動(dòng)新線程瓦阐;此外NSObject基類對(duì)象還提供了隱式快速創(chuàng)建NSThread線程的performSelector系列類別擴(kuò)展工具方法;NSThread還提供了一些靜態(tài)工具接口來(lái)控制當(dāng)前線程以及獲取當(dāng)前線程的一些信息篷牌。
下面以在一個(gè)UIViewController中為例展示NSThread的使用方法:
- (void)viewDidLoad {
????[super viewDidLoad];
/** NSThread靜態(tài)工具方法 **/
/* 1是否開(kāi)啟了多線程 */
????BOOL isMultiThreaded = [NSThread isMultiThreaded];
/* 2獲取當(dāng)前線程 */
????NSThread *currentThread = [NSThread currentThread];
/* 3獲取主線程 */
????NSThread *mainThread = [NSThread mainThread];
????NSLog(@"main thread");
/* 4睡眠當(dāng)前線程 */
/* 4.1線程睡眠5s鐘 */
????[NSThread sleepForTimeInterval:5];
/* 4.2線程睡眠到指定時(shí)間垄分,效果同上 */
????[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
/* 5退出當(dāng)前線程,注意不要在主線程調(diào)用娃磺,防止主線程被kill掉 */
????//[NSThread exit]; ????NSLog(@"main thread");
/** NSThread線程對(duì)象基本創(chuàng)建薄湿,target為入口函數(shù)所在的對(duì)象,selector為線程入口函數(shù) **/
/* 1線程實(shí)例對(duì)象創(chuàng)建與設(shè)置 */
????NSThread *newThread= [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
/*設(shè)置線程優(yōu)先級(jí)threadPriority(0~1.0)偷卧,即將被拋棄豺瘤,將使用qualityOfService代替 */
????newThread.threadPriority = 1.0;
????newThread.qualityOfService = NSQualityOfServiceUserInteractive;
/*開(kāi)啟線程 */
????[newThread start];
/* 2靜態(tài)方法快速創(chuàng)建并開(kāi)啟新線程 */
????[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
????[NSThread detachNewThreadWithBlock:^{
????????NSLog(@"block run...");
????}];
/** NSObejct基類隱式創(chuàng)建線程的一些靜態(tài)工具方法 **/
/* 1在當(dāng)前線程上執(zhí)行方法,延遲2s */
????[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
/* 2在指定線程上執(zhí)行方法听诸,不等待當(dāng)前線程 */
????[self performSelector:@selector(run) onThread:newThread withObject:nil waitUntilDone:NO];
/* 3后臺(tái)異步執(zhí)行函數(shù) */
????[self performSelectorInBackground:@selector(run) withObject:nil];
/* 4在主線程上執(zhí)行函數(shù) */
????[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];}
- (void)run {
????NSLog(@"run...");}
GCD大中央調(diào)度
GCD(Grand Central Dispatch)坐求,又叫大中央調(diào)度,對(duì)線程操作進(jìn)行了封裝晌梨,加入了很多新的特性桥嗤,內(nèi)部進(jìn)行了效率優(yōu)化,提供了簡(jiǎn)潔的C語(yǔ)言接口仔蝌,使用更加簡(jiǎn)單高效泛领,也是蘋(píng)果推薦的方式。
對(duì)于GCD多線程編程的理解需要結(jié)合實(shí)例和實(shí)踐去體會(huì)敛惊、總結(jié)渊鞋,網(wǎng)上有一篇國(guó)外的GCD詳細(xì)教程,結(jié)合案例通俗易懂瞧挤,可以幫助快速掌握锡宋,文章地址為:https://www.raywenderlich.com/60749/grand-central-dispatch-in-depth-part-1,另外國(guó)內(nèi)有業(yè)界人士對(duì)其做了翻譯特恬,中文版地址為:https://github.com/nixzhu/dev-blog/blob/master/2014-04-19-grand-central-dispatch-in-depth-part-1.md执俩,教程分兩部分,此處給出第一部分地址癌刽,第二部分可因此找到役首。這里對(duì)其進(jìn)行進(jìn)一步的總結(jié)提煉尝丐,整理出如下必會(huì)內(nèi)容,幫助快速掌握使用:
同步dispatch_sync與異步dispatch_async任務(wù)派發(fā)串行隊(duì)列與并發(fā)隊(duì)列dispatch_queue_tdispatch_once_t只執(zhí)行一次dispatch_after延后執(zhí)行
兩個(gè)關(guān)鍵概念
串行與并發(fā)(Serial和Concurrent):這個(gè)概念在創(chuàng)建操作隊(duì)列的時(shí)候有宏定義參數(shù)宋税,用來(lái)指定創(chuàng)建的是串行隊(duì)列還是并行隊(duì)列摊崭。
串行指的是隊(duì)列內(nèi)任務(wù)一個(gè)接一個(gè)的執(zhí)行讼油,任務(wù)之間要依次等待不可重合杰赛,且添加的任務(wù)按照先進(jìn)先出FIFO的順序執(zhí)行,但并不是指這就是單線程矮台,只是同一個(gè)串行隊(duì)列內(nèi)的任務(wù)需要依次等待排隊(duì)執(zhí)行避免出現(xiàn)競(jìng)態(tài)條件乏屯,但仍然可以創(chuàng)建多個(gè)串行隊(duì)列并行的執(zhí)行任務(wù),也就是說(shuō)瘦赫,串行隊(duì)列內(nèi)是串行的辰晕,串行隊(duì)列之間仍然是可以并行的,同一個(gè)串行隊(duì)列內(nèi)的任務(wù)的執(zhí)行順序是確定的(FIFO)确虱,且可以創(chuàng)建任意多個(gè)串行隊(duì)列含友;
并行指的是同一個(gè)隊(duì)列先后添加的多個(gè)任務(wù)可以同時(shí)并列執(zhí)行,任務(wù)之間不會(huì)相互等待校辩,且這些任務(wù)的執(zhí)行順序執(zhí)行過(guò)程不可預(yù)測(cè)窘问。
同步和異步任務(wù)派發(fā)(Synchronous和Asynchronous):GCD多線程編程時(shí)經(jīng)常會(huì)使用dispatch_async和dispatch_sync函數(shù)往指定隊(duì)列中添加任務(wù)塊,區(qū)別就是同步和異步宜咒。同步指的是阻塞當(dāng)前線程惠赫,要等添加的耗時(shí)任務(wù)塊block完成后,函數(shù)才能返回故黑,后面的代碼才可以繼續(xù)執(zhí)行儿咱。如果在主線上,則會(huì)發(fā)生阻塞场晶,用戶會(huì)感覺(jué)應(yīng)用不響應(yīng)混埠,這是要避免的。而有時(shí)需要使用同步任務(wù)的原因是想保證先后添加的任務(wù)要按照編寫(xiě)的邏輯順序依次執(zhí)行诗轻;異步指的是將任務(wù)添加到隊(duì)列后函數(shù)立刻返回岔冀,后面的代碼不用等待添加的任務(wù)完成返回即可繼續(xù)執(zhí)行。
dispatch_sync與dispatch_async
通過(guò)下面的代碼比較異步和同步任務(wù)的區(qū)別:
/* 1.提交異步任務(wù) */NSLog(@"開(kāi)始提交異步任務(wù):");dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/*耗時(shí)任務(wù)... */
[NSThread sleepForTimeInterval:10];});NSLog(@"異步任務(wù)提交成功概耻!");
/* 2.提交同步任務(wù) */NSLog(@"開(kāi)始提交同步任務(wù):");dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/*耗時(shí)任務(wù)... */
[NSThread sleepForTimeInterval:10];});NSLog(@"同步任務(wù)提交成功使套!");
打印結(jié)果:
2017-02-28 16:01:44.643 SingleView[19100:708069]開(kāi)始提交異步任務(wù):
2017-02-28 16:01:44.643 SingleView[19100:708069]異步任務(wù)提交成功!
2017-02-28 16:01:44.644 SingleView[19100:708069]開(kāi)始提交同步任務(wù):
2017-02-28 16:01:54.715 SingleView[19100:708069]同步任務(wù)提交成功鞠柄!
通過(guò)打印結(jié)果的時(shí)間可以看出侦高,異步任務(wù)提交后立即就執(zhí)行下一步打印提交成功了,不會(huì)阻礙當(dāng)前線程厌杜,提交的任務(wù)會(huì)在后臺(tái)去執(zhí)行奉呛;而提交同步任務(wù)要等到提交的后臺(tái)任務(wù)結(jié)束后才可以繼續(xù)執(zhí)行當(dāng)前線程的下一步计螺。此處在主線程上添加的同步任務(wù)就會(huì)阻塞主線程,導(dǎo)致后面界面的顯示要延遲瞧壮,影響用戶體驗(yàn)登馒。
dispatch_queue_t操作隊(duì)列主要有兩種,并發(fā)隊(duì)列和串行隊(duì)列咆槽,它們的區(qū)別上面已經(jīng)提到陈轿,具體創(chuàng)建的方法很簡(jiǎn)單,要提供兩個(gè)參數(shù)秦忿,一個(gè)是標(biāo)記該自定義隊(duì)列的唯一字符串麦射,另一個(gè)是指定串行隊(duì)列還是并發(fā)隊(duì)列的宏參數(shù):
/*創(chuàng)建一個(gè)并發(fā)隊(duì)列 */
dispatch_queue_t concurrent_queue = dispatch_queue_create("demo.gcd.concurrent_queue", DISPATCH_QUEUE_CONCURRENT);/*創(chuàng)建一個(gè)串行隊(duì)列 */
dispatch_queue_t serial_queue = dispatch_queue_create("demo.gcd.serial_queue", DISPATCH_QUEUE_SERIAL);
另外GCD還提供了幾個(gè)常用的全局隊(duì)列以及主隊(duì)列,獲取方法如下:
//獲取主隊(duì)列(在主線程上執(zhí)行)
dispatch_queue_t main_queue = dispatch_get_main_queue();//獲取不同優(yōu)先級(jí)的全局隊(duì)列(優(yōu)先級(jí)從高到低)
dispatch_queue_t queue_high = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_t queue_default = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t queue_low = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t queue_background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_once_t這個(gè)函數(shù)控制指定代碼只會(huì)被執(zhí)行一次灯谣,常用來(lái)實(shí)現(xiàn)單例模式潜秋,這里以單例模式實(shí)現(xiàn)的模板代碼為例展示dispatch_once_t的用法,其中的實(shí)例化語(yǔ)句只會(huì)被執(zhí)行一次:
+ (instancetype *)sharedInstance {
????static dispatch_once_t once = 0;
????static id sharedInstance = nil;
????dispatch_once(&once, ^{
//只實(shí)例化一次
????????sharedInstance = [[self alloc] init];
????});
????return sharedInstance;}
dispatch_after通過(guò)該函數(shù)可以讓要提交的任務(wù)在從提交開(kāi)始后的指定時(shí)間后執(zhí)行胎许,也就是定時(shí)延遲執(zhí)行提交的任務(wù)峻呛,使用方法很簡(jiǎn)單:
//定義延遲時(shí)間:3s
????dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
????dispatch_after(delay, dispatch_get_main_queue(), ^{
//要執(zhí)行的任務(wù)...
????});
dispatch_group_t組調(diào)度可以實(shí)現(xiàn)等待一組操作都完成后執(zhí)行后續(xù)操作,典型的例子是大圖片的下載辜窑,例如可以將大圖片分成幾塊同時(shí)下載钩述,等各部分都下載完后再后續(xù)將圖片拼接起來(lái),提高下載的效率谬擦。使用方法如下:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, queue, ^{ /*操作1 */ });dispatch_group_async(group, queue, ^{ /*操作2 */ });dispatch_group_async(group, queue, ^{ /*操作3 */ }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//后續(xù)操作... });
同步代碼到主線程
對(duì)于UI的更新代碼切距,必須要在主線程上執(zhí)行才會(huì)及時(shí)有效,當(dāng)當(dāng)前代碼不在主線程時(shí)惨远,需要將UI更新的部分代碼單獨(dú)同步到主線程谜悟,同步的方法有三種,可以使用NSThread類的performSelectorOnMainThread方法或者NSOperationQueue類的mainQueue主隊(duì)列來(lái)進(jìn)行同步北秽,但推薦直接使用GCD方法:
dispatch_async(dispatch_get_main_queue(), ^{
// UI更新代碼...
????});
NSOperation
NSOperation是基于GCD的一個(gè)抽象基類葡幸,將線程封裝成要執(zhí)行的操作,不需要管理線程的生命周期和同步贺氓,但比GCD可控性更強(qiáng)蔚叨,例如可以加入操作依賴(addDependency)、設(shè)置操作隊(duì)列最大可并發(fā)執(zhí)行的操作個(gè)數(shù)(setMaxConcurrentOperationCount)辙培、取消操作(cancel)等蔑水。NSOperation作為抽象基類不具備封裝我們的操作的功能,需要使用兩個(gè)它的實(shí)體子類:NSBlockOperation和NSInvocationOperation扬蕊,或者繼承NSOperation自定義子類搀别。
NSBlockOperation和NSInvocationOperation用法的主要區(qū)別是:前者執(zhí)行指定的方法,后者執(zhí)行代碼塊尾抑,相對(duì)來(lái)說(shuō)后者更加靈活易用歇父。NSOperation操作配置完成后便可調(diào)用start函數(shù)在當(dāng)前線程執(zhí)行蒂培,如果要異步執(zhí)行避免阻塞當(dāng)前線程則可以加入NSOperationQueue中異步執(zhí)行。他們的簡(jiǎn)單用法如下:
- (void)viewDidLoad {
????[super viewDidLoad];
/* NSInvocationOperation初始化 */
????NSInvocationOperation *invoOpertion = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
????[invoOpertion start];
/* NSBlockOperation初始化 */
????NSBlockOperation *blkOperation = [NSBlockOperation blockOperationWithBlock:^{
????????NSLog(@"NSBlockOperation");
????}];
????[blkOperation start];}
- (void)run {
????NSLog(@"NSInvocationOperation");}
另外NSBlockOperation可以后續(xù)繼續(xù)添加block執(zhí)行塊榜苫,操作啟動(dòng)后會(huì)在不同線程并發(fā)的執(zhí)行這些執(zhí)行快:
/* NSBlockOperation初始化 */
NSBlockOperation *blkOperation = [NSBlockOperation blockOperationWithBlock:^{
????NSLog(@"NSBlockOperationA");}];[blkOperation addExecutionBlock:^{
????NSLog(@"NSBlockOperationB");}];[blkOperation addExecutionBlock:^{
????NSLog(@"NSBlockOperationC");}];[blkOperation start];
2017-04-04 11:27:02.805 SingleView[12008:3666657] NSBlockOperationB
2017-04-04 11:27:02.805 SingleView[12008:3666742] NSBlockOperationC
2017-04-04 11:27:02.805 SingleView[12008:3666745] NSBlockOperationA
另外說(shuō)了NSOperation的可控性比GCD要強(qiáng)护戳,其中一個(gè)非常重要的特性是可以設(shè)置各操作之間的依賴,即強(qiáng)行規(guī)定操作A要在操作B完成之后才能開(kāi)始執(zhí)行垂睬,成為操作A依賴于操作B:
/*獲取主隊(duì)列(主線程) */
NSOperationQueue *queue = [NSOperationQueue mainQueue];/*創(chuàng)建a媳荒、b、c操作 */
NSOperation *c = [NSBlockOperation blockOperationWithBlock:^{
????NSLog(@"OperationC");}];
NSOperation *a = [NSBlockOperation blockOperationWithBlock:^{
????NSLog(@"OperationA");}];
NSOperation *b = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"OperationB");}];/*添加操作依賴羔飞,c依賴于a和b肺樟,這樣c一定會(huì)在a和b完成后才執(zhí)行檐春,即順序?yàn)椋篈逻淌、B、C */[c addDependency:a];[c addDependency:b];/* 添加操作a疟暖、b卡儒、c到操作隊(duì)列queue(特意將c在a和b之前添加) */[queue addOperation:c];[queue addOperation:a];[queue addOperation:b];
NSBlockOperation和NSInvocationOperation可以滿足多數(shù)情況下的編程需求,如果需求特殊則需要繼承NSOperation類自定義子類來(lái)更加靈活的實(shí)現(xiàn)俐巴。 ***
問(wèn)題:什么是線程骨望?它與進(jìn)程有什么區(qū)別?為什么要使用多線程欣舵?
線程是指程序在執(zhí)行過(guò)程中擎鸠,能夠執(zhí)行程序代碼的一個(gè)執(zhí)行單元。線程主要有四種狀態(tài):運(yùn)行缘圈、就緒劣光、掛起、結(jié)束糟把。
進(jìn)程是指一段正在執(zhí)行的程序绢涡。而線程有時(shí)候也被稱為輕量級(jí)進(jìn)程,是程序執(zhí)行的最小單元遣疯,一個(gè)進(jìn)程可以擁有多個(gè)線程雄可,各個(gè)線程之間共享程序的內(nèi)存空間(代碼段、數(shù)據(jù)段和堆空間)及一些進(jìn)程級(jí)的資源(例如打開(kāi)的文件)缠犀,但是各個(gè)線程擁有自己的検唬空間。
在操作系統(tǒng)級(jí)別上辨液,程序的執(zhí)行都是以進(jìn)程為單位的虐急,而每個(gè)進(jìn)程中通常都會(huì)有多個(gè)線程互不影響地并發(fā)執(zhí)行,那么為什么要使用多線程呢室梅?其實(shí)戏仓,多線程的使用為程序研發(fā)帶來(lái)了巨大的便利疚宇,具體而言,有以下幾個(gè)方面的內(nèi)容:
使用多線程可以減少程序的響應(yīng)時(shí)間赏殃。在單線程(單線程指的是程序執(zhí)行過(guò)程中只有一個(gè)有效操作的序列敷待,不同操作之間都有明確的執(zhí)行先后順序)的情況下,如果某個(gè)操作很耗時(shí)仁热,或者陷入長(zhǎng)時(shí)間的等待(如等待網(wǎng)絡(luò)響應(yīng))榜揖,此時(shí)程序?qū)⒉粫?huì)響應(yīng)鼠標(biāo)和鍵盤(pán)等操作,使用多線程后抗蠢,可以把這個(gè)耗時(shí)的線程分配到一個(gè)單獨(dú)的線程去執(zhí)行举哟,使得程序具備了更好的交互性。與進(jìn)程相比迅矛,線程的創(chuàng)建和切換開(kāi)銷更小妨猩。由于啟動(dòng)一個(gè)新的線程必須給這個(gè)線程分配獨(dú)立的地址空間,建立許多數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)線程代碼段秽褒、數(shù)據(jù)段等信息壶硅,而運(yùn)行于同一進(jìn)程內(nèi)的線程共享代碼段、數(shù)據(jù)段销斟,線程的啟動(dòng)或切換的開(kāi)銷比進(jìn)程要少很多庐椒。同時(shí)多線程在數(shù)據(jù)共享方面效率非常高。多CPU或多核計(jì)算機(jī)本身就具有執(zhí)行多線程的能力蚂踊,如果使用單個(gè)線程约谈,將無(wú)法重復(fù)利用計(jì)算機(jī)資源,造成資源的巨大浪費(fèi)犁钟。因此在多CPU計(jì)算機(jī)上使用多線程能提高CPU的利用率棱诱。使用多線程能簡(jiǎn)化程序的結(jié)構(gòu),使程序便于理解和維護(hù)特纤。一個(gè)非常復(fù)雜的進(jìn)程可以分成多個(gè)線程來(lái)執(zhí)行军俊。
問(wèn)題:列舉Cocoa中常見(jiàn)的幾種多線程的實(shí)現(xiàn),并談?wù)劧嗑€程安全的幾種解決辦法捧存,一般什么地方會(huì)用到多線程?
問(wèn)的是三個(gè)層次的多線程編程實(shí)現(xiàn)粪躬;線程鎖的使用;
只在主線程刷新訪問(wèn)UI如果要防止資源搶奪昔穴,得用synchronized進(jìn)行加鎖保護(hù)如果異步操作要保證線程安全等問(wèn)題,盡量使用GCD(有些函數(shù)默認(rèn) 就是安全的)
問(wèn)題:OC中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼镰官,方法是什么?如果想延時(shí)執(zhí)行代碼、方法又是什么? ***
問(wèn)題:在iphone上有兩件事情要做,請(qǐng)問(wèn)是在一個(gè)線程里按順序做效率高還是兩個(gè)線程里做效率高吗货?為什么泳唠?
這里的效率高指的是時(shí)間上效率高,也就是希望在最短的時(shí)間內(nèi)完成所有任務(wù)宙搬,兩件事情按順序做意味著串行執(zhí)行笨腥,第二件事情要等待第一件事情結(jié)束后才可開(kāi)始拓哺,效率相對(duì)很低。但如果利用兩個(gè)線程讓兩件事情能夠并發(fā)執(zhí)行脖母,則時(shí)間上效率會(huì)大大提高士鸥。***
問(wèn)題:runloop是什么?在主線程中的某個(gè)函數(shù)里調(diào)用了異步函數(shù)谆级,怎樣block當(dāng)前線程,且還能響應(yīng)當(dāng)前線程的timer事件烤礁,touch事件等? ***
問(wèn)題:對(duì)比在OS X和iOS中實(shí)現(xiàn)并發(fā)性的不同方法肥照。
在iOS中有三種方法可以實(shí)現(xiàn)并發(fā)性:threads脚仔、dispatch queues和operation queues。
threads的缺點(diǎn)是開(kāi)發(fā)者要自己創(chuàng)建合適的并發(fā)解決方案舆绎,要決定創(chuàng)建多少線程并且要根據(jù)情況動(dòng)態(tài)調(diào)整線程的數(shù)量鲤脏。并且應(yīng)用還要為創(chuàng)建和維護(hù)線程承擔(dān)主要代價(jià), 因此OS X和IOS系統(tǒng)并不是依靠線程來(lái)解決并發(fā)問(wèn)題的亿蒸,而是采用了異步設(shè)計(jì)的途徑凑兰。
其中一個(gè)開(kāi)啟異步任務(wù)的技術(shù)是GCD(Grand Central Dispatch)掌桩,讓系統(tǒng)層次來(lái)對(duì)線程進(jìn)行管理边锁。開(kāi)發(fā)者要做的就是定義要執(zhí)行的任務(wù)并將之添加到合適的dispatch分派隊(duì)列。GCD會(huì)負(fù)責(zé)創(chuàng)建需要的線程并安排任務(wù)在這些線程上運(yùn)行波岛。所有的dispatch隊(duì)列都是先進(jìn)先出(FIFO)隊(duì)列結(jié)構(gòu)茅坛,所以任務(wù)的執(zhí)行順序是和添加順序是一致的。
operation queues和并發(fā)dispatch隊(duì)列一樣都是通過(guò)Cocoa框架的NSOperationQueue類實(shí)現(xiàn)的则拷,不過(guò)它并不一定是按照先進(jìn)先出的順序執(zhí)行任務(wù)的贡蓖,而是支持創(chuàng)建更復(fù)雜的執(zhí)行順序圖來(lái)管理任務(wù)的執(zhí)行順序。 ***
問(wèn)題:操作隊(duì)列(NSOperation queue)是什么?解釋NSOperationQueue串行操作隊(duì)列和并行操作隊(duì)列的區(qū)別煌茬。 ***
問(wèn)題:用戶下載一個(gè)圖片斥铺,圖片很大,需要分成很多份進(jìn)行下載坛善,使用GCD應(yīng)該如何實(shí)現(xiàn)晾蜘?使用什么隊(duì)列?
使用Dispatch Group追加block到Global Group Queue眠屎,這些block如果全部執(zhí)行完畢剔交,就會(huì)執(zhí)行Main Dispatch Queue中的結(jié)束處理的block。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, queue, ^{ /*加載圖片1 */ });dispatch_group_async(group, queue, ^{ /*加載圖片2 */ });dispatch_group_async(group, queue, ^{ /*加載圖片3 */ }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//合并圖片 });
問(wèn)題:Dispatch_barrier_(a)sync的作用?通過(guò)Dispatch_barrier_async添加的操作會(huì)暫時(shí)阻塞當(dāng)前隊(duì)列改衩,即等待前面的并發(fā)操作都完成后執(zhí)行該阻塞操作岖常,待其完成后后面的并發(fā)操作才可繼續(xù)『剑可以將其比喻為一座霸道的獨(dú)木橋竭鞍,是并發(fā)隊(duì)列中的一個(gè)并發(fā)障礙點(diǎn)板惑,臨時(shí)阻塞并獨(dú)占.
可見(jiàn)使用Dispatch_barrier_async可以實(shí)現(xiàn)類似dispatch_group_t組調(diào)度的效果,同時(shí)主要的作用是避免數(shù)據(jù)競(jìng)爭(zhēng),高效訪問(wèn)數(shù)據(jù)偎快。
/*創(chuàng)建并發(fā)隊(duì)列 */
dispatch_queue_t concurrentQueue = dispatch_queue_create("test.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);/*添加兩個(gè)并發(fā)操作A和B洒放,即A和B會(huì)并發(fā)執(zhí)行 */dispatch_async(concurrentQueue, ^(){
????NSLog(@"OperationA");});dispatch_async(concurrentQueue, ^(){
NSLog(@"OperationB");});/*添加barrier障礙操作,會(huì)等待前面的并發(fā)操作結(jié)束滨砍,并暫時(shí)阻塞后面的并發(fā)操作直到其完成 */dispatch_barrier_async(concurrentQueue, ^(){
NSLog(@"OperationBarrier!");});/*繼續(xù)添加并發(fā)操作C和D往湿,要等待barrier障礙操作結(jié)束才能開(kāi)始 */dispatch_async(concurrentQueue, ^(){
????NSLog(@"OperationC");});dispatch_async(concurrentQueue, ^(){
????NSLog(@"OperationD");});
2017-04-04 12:25:02.344 SingleView[12818:3694480] OperationB
2017-04-04 12:25:02.344 SingleView[12818:3694482] OperationA
2017-04-04 12:25:02.345 SingleView[12818:3694482] OperationBarrier!
2017-04-04 12:25:02.345 SingleView[12818:3694482] OperationD
2017-04-04 12:25:02.345 SingleView[12818:3694480] OperationC
問(wèn)題:用過(guò)NSOperationQueue嗎?如果用過(guò)或者了解的話,為什么要使用 NSOperationQueue惋戏?實(shí)現(xiàn)了什么?簡(jiǎn)述它和GCD的區(qū)別和類似的地方(提示:可以從兩者的實(shí)現(xiàn)機(jī)制和適用范圍來(lái)述)领追。
使用NSOperationQueue用來(lái)管理子類化的NSOperation對(duì)象,控制其線程并發(fā)數(shù)目响逢。GCD和NSOperation都可以實(shí)現(xiàn)對(duì)線程的管理绒窑,區(qū)別是NSOperation和NSOperationQueue是多線程的面向?qū)ο蟪橄蟆m?xiàng)目中使用NSOperation的優(yōu)點(diǎn)是 NSOperation是對(duì)線程的高度抽象舔亭,在項(xiàng)目中使用它些膨,會(huì)使項(xiàng)目的程序結(jié)構(gòu)更好,子類化 NSOperation的設(shè)計(jì)思路钦铺,是具有面向?qū)ο蟮膬?yōu)點(diǎn)(復(fù)用订雾、封裝),使得實(shí)現(xiàn)是多線程支持矛洞,而接口簡(jiǎn)單洼哎,建議在復(fù)雜項(xiàng)目中使用。項(xiàng)目中使用GCD的優(yōu)點(diǎn)是GCD本身非常簡(jiǎn)單沼本、易用噩峦,對(duì)于不復(fù)雜的多線程操作,會(huì)節(jié)省代碼量抽兆,而B(niǎo)lock參數(shù)的使用识补,會(huì)使代碼更為易讀,建議在簡(jiǎn)單項(xiàng)目中使用辫红。
GCD是純C語(yǔ)言的API凭涂,NSOperationQueue是基于GCD的OC版本封裝GCD只支持FIFO的隊(duì)列,NSOperationQueue可以很方便地調(diào)整執(zhí)行順 序厉熟、設(shè)置最大并發(fā)數(shù)量NSOperationQueue可以在輕松在Operation間設(shè)置依賴關(guān)系导盅,而GCD 需要寫(xiě)很多的代碼才能實(shí)現(xiàn)NSOperationQueue支持KVO,可以監(jiān)測(cè)operation是否正在執(zhí)行 (isExecuted)揍瑟、是否結(jié)束(isFinished)白翻,是否取消(isCanceld) 5> GCD的執(zhí)行速度比NSOperationQueue快 任務(wù)之間不太互相依賴:GCD 任務(wù)之間有依賴\或者要監(jiān)聽(tīng)任務(wù)的執(zhí)行情況:NSOperationQueue
問(wèn)題:在使用GCD以及block時(shí)要注意些什么?它們兩是一回事兒么?block在ARC中和傳統(tǒng)的MRC中的行為和用法有沒(méi)有什么區(qū)別,需要注意些什么?
使用block是要注意,若將block做函數(shù)參數(shù)時(shí)滤馍,需要把它放到最后岛琼,GCD是Grand Central Dispatch,是一個(gè)對(duì)線程開(kāi)源類庫(kù)巢株,而B(niǎo)lock是閉包槐瑞,是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。
問(wèn)題:在項(xiàng)目什么時(shí)候選擇使用GCD阁苞,什么時(shí)候選擇 NSOperation?
項(xiàng)目中使用NSOperation的優(yōu)點(diǎn)是NSOperation是對(duì)線程的高度抽象困檩,在項(xiàng)目中使用它,會(huì)使項(xiàng)目的程序結(jié)構(gòu)更好那槽,子類化NSOperation的設(shè)計(jì)思路悼沿,是具有面向?qū)ο蟮膬?yōu)點(diǎn)(復(fù)用、封裝)骚灸,使得實(shí)現(xiàn)多線程支持糟趾,而接口簡(jiǎn)單,建議在復(fù)雜項(xiàng)目中使用甚牲。
項(xiàng)目中使用GCD的優(yōu)點(diǎn)是GCD本身非常簡(jiǎn)單义郑、易用,對(duì)于不復(fù)雜的多線程操作丈钙,會(huì)節(jié)省代碼量非驮,而B(niǎo)lock參數(shù)的使用,會(huì)是代碼更為易讀著恩,建議在簡(jiǎn)單項(xiàng)目中使用院尔。
問(wèn)題:對(duì)于a,b,c三個(gè)線程,如何使用用NSOpertion和NSOpertionQueue實(shí)現(xiàn)執(zhí)行完a,b后再執(zhí)行c?
可以通過(guò)NSOpertion的依賴特性實(shí)現(xiàn)該需求喉誊,即讓c依賴于a和b,這樣只有a和b都執(zhí)行完后纵顾,c才可以開(kāi)始執(zhí)行:
/*獲取主隊(duì)列(主線程) */
NSOperationQueue *queue = [NSOperationQueue mainQueue];/*創(chuàng)建a伍茄、b、c操作 */
NSOperation *c = [NSBlockOperation blockOperationWithBlock:^{
????NSLog(@"Operation C Start!");
????// ... ...}];
NSOperation *a = [NSBlockOperation blockOperationWithBlock:^{
????NSLog(@"Operation A Start!");
????[NSThread sleepForTimeInterval:3.0];
????NSLog(@"Operation A Done!");}];
NSOperation *b = [NSBlockOperation blockOperationWithBlock:^{
????NSLog(@"Operation B Start!");
????[NSThread sleepForTimeInterval:3.0];
NSLog(@"Operation B Done!");}];/*添加操作依賴施逾,c依賴于a和b */[c addDependency:a];[c addDependency:b];/* 添加操作a敷矫、b、c到操作隊(duì)列queue(特意將c在a和b之前添加) */[queue addOperation:c];[queue addOperation:a];[queue addOperation:b];
2017-03-18 13:51:37.770 SingleView[15073:531745] Operation A Start!
2017-03-18 13:51:40.772 SingleView[15073:531745] Operation A Done!
2017-03-18 13:51:40.775 SingleView[15073:531745] Operation B Start!
2017-03-18 13:51:43.799 SingleView[15073:531745] Operation B Done!
2017-03-18 13:51:43.800 SingleView[15073:531745] Operation C Start!
問(wèn)題:GCD內(nèi)部是怎么實(shí)現(xiàn)的?
iOS和OS X的核心是XNU內(nèi)核汉额,GCD是基于XNU內(nèi)核實(shí)現(xiàn)的GCD的API全部在libdispatch庫(kù)中GCD的底層實(shí)現(xiàn)主要有Dispatch Queue和Dispatch SourceDispatch Queue :管理block(操作)Dispatch Source :處理事件
問(wèn)題:既然提到GCD曹仗,那么問(wèn)一下在使用GCD以及 block 時(shí)要注意些什么?它們兩是一回事兒么? block 在 ARC 中和傳統(tǒng)的 MRC 中的行為和用法有 沒(méi)有什么區(qū)別,需要注意些什么?
Block的使用注意:
block的內(nèi)存管理防止循環(huán)retian非ARC(MRC):__blockARC:__weak__unsafe_unretained
問(wèn)題:下面代碼有什么問(wèn)題蠕搜?
int main(int argc, const char * argv[]) {
????NSLog(@"1");
????dispatch_sync(dispatch_get_main_queue(), ^{
????????NSLog(@"2");
????});
????NSLog(@"3");
????return 0;}
main函數(shù)中第二句代碼在主線程上使用了dispatch_sync同步向主線程派發(fā)任務(wù)怎茫,而同步派發(fā)要等到任務(wù)完成后才能返回,阻塞當(dāng)前線程。也就是說(shuō)執(zhí)行到此處轨蛤,主線程被阻塞蜜宪,同時(shí)又要等主線程執(zhí)行完成該任務(wù),造成主線程自身的等待循環(huán)祥山,也就是死鎖圃验。程序運(yùn)行到此處會(huì)崩潰。將dispatch_sync改為dispatch_async異步派發(fā)任務(wù)即可避免死鎖缝呕,或者將任務(wù)派發(fā)到其他隊(duì)列上而不是主隊(duì)列澳窑。
問(wèn)題:簡(jiǎn)單介紹下NSURLConnection類,以及+sendSynchronousRequest:returningResponse:error:與– initWithRequest:delegate:兩個(gè)方法的區(qū)別?
NSURLConnection主要用于網(wǎng)絡(luò)鏈接請(qǐng)求供常,提供了異步和同步兩種請(qǐng)求方式照捡,異步請(qǐng)求會(huì)新創(chuàng)建一個(gè)線程單獨(dú)用于之后的下載,不會(huì)阻塞當(dāng)前調(diào)用的線程话侧;同步請(qǐng)求會(huì)阻塞當(dāng)前調(diào)用線程栗精,等待它下載結(jié)束,如果在主線程上進(jìn)行同步請(qǐng)求則會(huì)阻塞主線程瞻鹏,造成界面卡頓悲立。
+sendSynchronousRequest:returningResponse:error:是同步請(qǐng)求數(shù)據(jù),會(huì)阻塞當(dāng)前的線程新博,直到request返回response薪夕;
–initWithRequest:delegate:是異步請(qǐng)求數(shù)據(jù),不會(huì)足阻塞當(dāng)前線程赫悄,當(dāng)數(shù)據(jù)請(qǐng)求結(jié)束后會(huì)通過(guò)代理回到主線程原献,并通知它委托的對(duì)象。
問(wèn)題:UIKit類要在哪一個(gè)應(yīng)用線程上使用埂淮?
UIKit的界面類只能在主線程上使用姑隅,對(duì)界面進(jìn)行更新,多線程環(huán)境中要對(duì)界面進(jìn)行更新必須要切換到主線程上倔撞。 ***
問(wèn)題:以下代碼有什么問(wèn)題讲仰?如何修復(fù)?
@interface TTWaitController : UIViewController
@property (strong, nonatomic) UILabel *alert;
@end
@implementation TTWaitController
- (void)viewDidLoad{
????CGRect frame = CGRectMake(20, 200, 200, 20);
????self.alert = [[UILabel alloc] initWithFrame:frame];
????self.alert.text = @"Please wait 10 seconds...";
????self.alert.textColor = [UIColor whiteColor];
????[self.view addSubview:self.alert];
????NSOperationQueue *waitQueue = [[NSOperationQueue alloc] init];
????[waitQueue addOperationWithBlock:^{
????????[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
????????self.alert.text = @"Thanks!";
????}];}
@end
@implementation TTAppDelegate
- (BOOL)application:(UIApplication *)application
??didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
????self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
????self.window.rootViewController = [[TTWaitController alloc] init];
????[self.window makeKeyAndVisible];
????return YES;}
這段代碼是想提醒用戶等待10s痪蝇,10s后在標(biāo)簽上顯示“Thanks”鄙陡,但多線程代碼部分NSOperationQueue的addOperationWithBlock函數(shù)不能保證block里面的語(yǔ)句是在主線程中運(yùn)行的,UILabel顯示文字屬于UI更新躏啰,必須要在主線程進(jìn)行趁矾,否則會(huì)有未知的操作,無(wú)法在界面上及時(shí)正常顯示给僵。
解決方法是將UI更新的代碼寫(xiě)在主線程上即可毫捣,代碼同步到主線程上主要有三種方法:NSThread、NSOperationQueue和GCD,三個(gè)層次的多線程都可以獲取主線程并同步培漏。
NSThread級(jí)主線程同步:
NSOperationQueue *waitQueue = [[NSOperationQueue alloc] init];[waitQueue addOperationWithBlock:^{
????[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
//同步到主線程 ????[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO];}];
/** * UI更新函數(shù) */- (void)updateUI {
????self.alert.text = @"Thanks!";}
NSOperationQueue級(jí)主線程同步:
NSOperationQueue *waitQueue = [[NSOperationQueue alloc] init];[waitQueue addOperationWithBlock:^{
????[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
//同步到主線程 ????[[NSOperationQueue mainQueue] addOperationWithBlock:^{
????????self.alert.text = @"Thanks!";
????}];}];
GCD級(jí)主線程同步:
NSOperationQueue *waitQueue = [[NSOperationQueue alloc] init];[waitQueue addOperationWithBlock:^{
????[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
//同步到主線程 ????dispatch_async(dispatch_get_main_queue(), ^{
????????self.alert.text = @"Thanks!";
????});}];