首先要說(shuō)明一下纪蜒,下面所有面試題調(diào)用的方法(比如第一個(gè)面試題調(diào)用的方法是interview1)都是在主線程中調(diào)用的。
作為一個(gè)開(kāi)發(fā)者莲趣,有一個(gè)學(xué)習(xí)的氛圍跟一個(gè)交流圈子特別重要样悟,這是一個(gè)我的iOS開(kāi)發(fā)交流群:130 595 548,不管你是大牛還是小白都?xì)g迎入駐 梆暖,讓我們一起進(jìn)步伞访,共同發(fā)展!(群內(nèi)會(huì)免費(fèi)提供一些群主收藏的免費(fèi)學(xué)習(xí)書(shū)籍資料以及整理好的幾百道面試題和答案文檔:洳怠)
- 面試題1
- (void)interview1{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"1---%@",[NSThread currentThread]);
[self performSelector:@selector(test1) withObject:nil afterDelay:.0f];
NSLog(@"3---%@",[NSThread currentThread]);
});
}
- (void)test1{
NSLog(@"2---%@",[NSThread currentThread]);
}
// ***************打印結(jié)果***************
2019-12-30 17:37:58.427558+0800 MultithreadingDemo[39113:4277962] 1---<NSThread: 0x600001922d40>{number = 6, name = (null)}
2019-12-30 17:37:58.427659+0800 MultithreadingDemo[39113:4277962] 3---<NSThread: 0x600001922d40>{number = 6, name = (null)}
解釋: performSelector:withObject:afterDelay:的本質(zhì)是往Runloop中添加定時(shí)器(即使延時(shí)時(shí)間是0秒)厚掷。由于異步函數(shù)dispatch_async是開(kāi)啟一個(gè)新的子線程去執(zhí)行任務(wù),而子線程默認(rèn)是沒(méi)有啟動(dòng)Runloop的级解,所以并不會(huì)執(zhí)行test1方法冒黑。
我們可以手動(dòng)啟動(dòng)runloop來(lái)確保test1被調(diào)用,也就是在block里面添加一行代碼[[NSRunLoop currentRunLoop] run];勤哗。
如果把異步函數(shù)改為同步函數(shù)抡爹,我們?cè)賮?lái)看下運(yùn)行結(jié)果:
- (void)interview1{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_sync(queue, ^{
NSLog(@"1---%@",[NSThread currentThread]);
[self performSelector:@selector(test1) withObject:nil afterDelay:.0f];
NSLog(@"3---%@",[NSThread currentThread]);
});
}
- (void)test1{
NSLog(@"2---%@",[NSThread currentThread]);
}
// ***************打印結(jié)果***************
2019-12-30 17:47:01.936609+0800 MultithreadingDemo[39150:4282068] 1---<NSThread: 0x6000009660c0>{number = 1, name = main}
2019-12-30 17:47:01.936724+0800 MultithreadingDemo[39150:4282068] 3---<NSThread: 0x6000009660c0>{number = 1, name = main}
2019-12-30 17:47:01.936904+0800 MultithreadingDemo[39150:4282068] 2---<NSThread: 0x6000009660c0>{number = 1, name = main}
解釋: 同步函數(shù)添加的任務(wù)是在當(dāng)前線程中執(zhí)行,當(dāng)前線程就是主線程芒划,而主線程的Runloop是啟動(dòng)的冬竟,所以test1會(huì)調(diào)用。雖然延遲時(shí)間時(shí)0秒民逼,但是添加到Runloop中的計(jì)時(shí)器不是立馬觸發(fā)的泵殴,而是要先喚醒Runloop,這是需要消耗一定時(shí)間的拼苍,所以會(huì)先打印3再打印2笑诅。
我們?cè)侔裵erformSelector:withObject:afterDelay:替換成performSelector:withObject:看看運(yùn)行結(jié)果:
- (void)interview1{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"1---%@",[NSThread currentThread]);
[self performSelector:@selector(test1) withObject:nil];
NSLog(@"3---%@",[NSThread currentThread]);
});
}
- (void)test1{
NSLog(@"2---%@",[NSThread currentThread]);
}
// ***************打印結(jié)果***************
2019-12-30 17:54:18.072035+0800 MultithreadingDemo[39183:4285659] 1---<NSThread: 0x60000303c300>{number = 3, name = (null)}
2019-12-30 17:54:18.072136+0800 MultithreadingDemo[39183:4285659] 2---<NSThread: 0x60000303c300>{number = 3, name = (null)}
2019-12-30 17:54:18.072215+0800 MultithreadingDemo[39183:4285659] 3---<NSThread: 0x60000303c300>{number = 3, name = (null)}
解釋: performSelector:withObject:函數(shù)是不涉及到計(jì)時(shí)器的,所以不會(huì)添加到Runloop中,所以是按照1吆你、2弦叶、3的順序執(zhí)行。
注意:performSelector系列方法中只要是方法名中包含afterDelay妇多、waitUntilDone的都是和計(jì)時(shí)器有關(guān)的伤哺,都要注意前面出現(xiàn)的這些問(wèn)題。
- 面試題2
- (void)interview2{
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSLog(@"1---%@",[NSThread currentThread]);
}];
[thread start];
[self performSelector:@selector(test2) onThread:thread withObject:nil waitUntilDone:YES];
}
- (void)test2{
NSLog(@"2---%@",[NSThread currentThread]);
}
// ***************運(yùn)行結(jié)果(閃退)***************
2019-12-31 08:36:07.132133+0800 MultithreadingDemo[40268:4493885] 1---<NSThread: 0x6000010d9880>{number = 6, name = (null)}
2019-12-31 08:36:07.432190+0800 MultithreadingDemo[40268:4493455] *** Terminating app due to uncaught exception 'NSDestinationInvalidException', reason: '*** -[Interview performSelector:onThread:withObject:waitUntilDone:modes:]: target thread exited while waiting for the perform'
解釋: 從運(yùn)行結(jié)果可以看出閃退的原因是target thread exited(目標(biāo)線程退出)砌梆。因?yàn)閠est2方法是在線程thread上執(zhí)行的默责,但是線程thread在執(zhí)行完NSLog(@"1---%@",[NSThread currentThread]);這句代碼后就結(jié)束了,所以等到執(zhí)行test2方法時(shí)線程thread已經(jīng)不存在了(嚴(yán)格來(lái)說(shuō)是線程對(duì)象是還存在的咸包,只是已經(jīng)失活了桃序,不能再執(zhí)行任務(wù)了)。
如果想要代碼能正常運(yùn)行烂瘫,我們可以利用Runloop知識(shí)來(lái)泵叫埽活線程。先向當(dāng)前runloop中添加一個(gè)source(如果runloop中一個(gè)source坟比、NSTime或Obserer都沒(méi)有的話就會(huì)退出)芦鳍,然后啟動(dòng)runloop。也就是在線程thread的block中添加2行代碼葛账,如下所示:
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSLog(@"1---%@",[NSThread currentThread]);
// 線程蹦疲活
// 先向當(dāng)前runloop中添加一個(gè)source(如果runloop中一個(gè)source、NSTime或Obserer都沒(méi)有的話就會(huì)退出)
[[NSRunLoop currentRunLoop] addPort:[NSPort new] forMode:NSRunLoopCommonModes];
// 然后啟動(dòng)runloop
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}];
- 面試題3
- (void)interview3{
NSLog(@"執(zhí)行任務(wù)1--%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"執(zhí)行任務(wù)2--%@",[NSThread currentThread]);
});
NSLog(@"執(zhí)行任務(wù)3--%@",[NSThread currentThread]);
}
運(yùn)行結(jié)果:
執(zhí)行完任務(wù)1后就被卡死了籍琳。
解釋:
interview3方法是在主線程中執(zhí)行菲宴,執(zhí)行完任務(wù)1后,通過(guò)同步函數(shù)向主隊(duì)列(串行隊(duì)列)添加任務(wù)2趋急,由于同步添加的任務(wù)必須馬上執(zhí)行喝峦,而串行隊(duì)列中當(dāng)前任務(wù)(interview3)還沒(méi)執(zhí)行完,就沒(méi)法安排任務(wù)2執(zhí)行呜达,所以要等當(dāng)前正在執(zhí)行的任務(wù)(interview3)執(zhí)行完了后才能執(zhí)行任務(wù)2谣蠢,而interview3又要等任務(wù)2執(zhí)行完了才會(huì)繼續(xù)往下執(zhí)行,這樣就造成了相互等待而死鎖查近。
- 面試題4
- (void)interview4{
NSLog(@"執(zhí)行任務(wù)1--%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"執(zhí)行任務(wù)2--%@",[NSThread currentThread]);
});
NSLog(@"執(zhí)行任務(wù)3--%@",[NSThread currentThread]);
}
// ***************打印結(jié)果***************
2019-12-31 10:06:37.782135+0800 MultithreadingDemo[41281:4538099] 執(zhí)行任務(wù)1--<NSThread: 0x600003cd1d80>{number = 1, name = main}
2019-12-31 10:06:37.782244+0800 MultithreadingDemo[41281:4538099] 執(zhí)行任務(wù)3--<NSThread: 0x600003cd1d80>{number = 1, name = main}
2019-12-31 10:06:37.782574+0800 MultithreadingDemo[41281:4538099] 執(zhí)行任務(wù)2--<NSThread: 0x600003cd1d80>{number = 1, name = main}
解釋:
這和前面一個(gè)面試題相比只是把同步函數(shù)換成了異步函數(shù)眉踱。執(zhí)行完任務(wù)1后,通過(guò)異步函數(shù)添加任務(wù)2霜威,雖然異步函數(shù)有開(kāi)啟子線程的能力谈喳,但是由于是在主隊(duì)列中,主隊(duì)列的任務(wù)都是在主線程中執(zhí)行侥祭,所以并不會(huì)開(kāi)啟子線程叁执。由于是異步函數(shù)添加的任務(wù)2,所以不必等待任務(wù)2就可以繼續(xù)往下執(zhí)行矮冬,等當(dāng)前任務(wù)(interview4)完成后串行隊(duì)列再安排執(zhí)行任務(wù)2谈宛。所以并不會(huì)造成死鎖。
- 面試題5
- (void)interview5{
NSLog(@"執(zhí)行任務(wù)1--%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"執(zhí)行任務(wù)2--%@",[NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"執(zhí)行任務(wù)3--%@",[NSThread currentThread]);
});
NSLog(@"執(zhí)行任務(wù)4--%@",[NSThread currentThread]);
});
NSLog(@"執(zhí)行任務(wù)5--%@",[NSThread currentThread]);
// ***************打印結(jié)果(打印1胎署、5吆录、2后卡死)***************
2019-12-31 10:45:29.071774+0800 MultithreadingDemo[41379:4551961] 執(zhí)行任務(wù)1--<NSThread: 0x6000038460c0>{number = 1, name = main}
2019-12-31 10:45:29.071923+0800 MultithreadingDemo[41379:4551961] 執(zhí)行任務(wù)5--<NSThread: 0x6000038460c0>{number = 1, name = main}
2019-12-31 10:45:29.071932+0800 MultithreadingDemo[41379:4552048] 執(zhí)行任務(wù)2--<NSThread: 0x600003824f40>{number = 6, name = (null)}
}
解釋: 首先打印任務(wù)1,然后自己創(chuàng)建了一個(gè)串行隊(duì)列琼牧,并通過(guò)異步函數(shù)向這個(gè)隊(duì)列中添加一個(gè)任務(wù)塊(block1)恢筝,異步函數(shù)會(huì)開(kāi)啟一個(gè)子線程并將block1放入子線程中去執(zhí)行,開(kāi)啟子線程是要耗時(shí)的巨坊,而且異步任務(wù)不需要等待就可以繼續(xù)執(zhí)行它后面的代碼撬槽,所以打印任務(wù)5在block1前面執(zhí)行。
再來(lái)看block1任務(wù)塊趾撵,先打印任務(wù)2侄柔,然后通過(guò)同步函數(shù)添加的block2任務(wù)塊需要立馬執(zhí)行,而block1所在的隊(duì)列是串行隊(duì)列占调,block1任務(wù)塊還沒(méi)執(zhí)行完暂题,所以要先等block1執(zhí)行,而block1又要等block2執(zhí)行完了才能繼續(xù)往下執(zhí)行究珊,所以就造成了相互等待而死鎖薪者。
- 面試題6
- (void)interview6{
NSLog(@"執(zhí)行任務(wù)1--%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("myqueu2", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"執(zhí)行任務(wù)2--%@",[NSThread currentThread]);
dispatch_sync(queue2, ^{
NSLog(@"執(zhí)行任務(wù)3--%@",[NSThread currentThread]);
});
NSLog(@"執(zhí)行任務(wù)4--%@",[NSThread currentThread]);
});
NSLog(@"執(zhí)行任務(wù)5--%@",[NSThread currentThread]);
}
// ***************打印結(jié)果***************
2019-12-31 11:01:47.812260+0800 MultithreadingDemo[41405:4557566] 執(zhí)行任務(wù)1--<NSThread: 0x600002836180>{number = 1, name = main}
2019-12-31 11:01:47.812470+0800 MultithreadingDemo[41405:4557566] 執(zhí)行任務(wù)5--<NSThread: 0x600002836180>{number = 1, name = main}
2019-12-31 11:01:47.812488+0800 MultithreadingDemo[41405:4557684] 執(zhí)行任務(wù)2--<NSThread: 0x600002830980>{number = 5, name = (null)}
2019-12-31 11:01:47.812567+0800 MultithreadingDemo[41405:4557684] 執(zhí)行任務(wù)3--<NSThread: 0x600002830980>{number = 5, name = (null)}
2019-12-31 11:01:47.812648+0800 MultithreadingDemo[41405:4557684] 執(zhí)行任務(wù)4--<NSThread: 0x600002830980>{number = 5, name = (null)}
解釋:
這個(gè)和面試題5相比就是新加了一個(gè)隊(duì)列(不管是串行隊(duì)列還是并發(fā)隊(duì)列都一樣),block1任務(wù)塊和block2任務(wù)塊分別放在不同的隊(duì)列中剿涮。
先打印任務(wù)1再打印任務(wù)5和前面是一樣的言津。然后異步函數(shù)會(huì)開(kāi)啟子線程去執(zhí)行block1任務(wù)塊,block1中先打印任務(wù)2幔虏,然后通過(guò)同步函數(shù)向另一個(gè)隊(duì)列中添加block2任務(wù)塊纺念,由于兩個(gè)block屬于不同的隊(duì)列,block2可以立馬被安排執(zhí)行而不會(huì)死鎖想括,所以接著是打印任務(wù)3陷谱,最后打印任務(wù)4。
- 面試題7
- (void)interview7{
NSLog(@"執(zhí)行任務(wù)1--%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"執(zhí)行任務(wù)2--%@",[NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"執(zhí)行任務(wù)3--%@",[NSThread currentThread]);
});
NSLog(@"執(zhí)行任務(wù)4--%@",[NSThread currentThread]);
});
NSLog(@"執(zhí)行任務(wù)5--%@",[NSThread currentThread]);
}
// ***************打印結(jié)果***************
2019-12-31 11:14:27.690008+0800 MultithreadingDemo[41445:4562142] 執(zhí)行任務(wù)1--<NSThread: 0x6000011badc0>{number = 1, name = main}
2019-12-31 11:14:27.690102+0800 MultithreadingDemo[41445:4562142] 執(zhí)行任務(wù)5--<NSThread: 0x6000011badc0>{number = 1, name = main}
2019-12-31 11:14:27.690122+0800 MultithreadingDemo[41445:4562301] 執(zhí)行任務(wù)2--<NSThread: 0x6000011f5900>{number = 3, name = (null)}
2019-12-31 11:14:27.690202+0800 MultithreadingDemo[41445:4562301] 執(zhí)行任務(wù)3--<NSThread: 0x6000011f5900>{number = 3, name = (null)}
2019-12-31 11:14:27.690285+0800 MultithreadingDemo[41445:4562301] 執(zhí)行任務(wù)4--<NSThread: 0x6000011f5900>{number = 3, name = (null)}
解釋:
這個(gè)和面試題5相比是把串行隊(duì)列換成了并發(fā)隊(duì)列瑟蜈。
先打印任務(wù)1再打印任務(wù)5和前面是一樣的烟逊。然后異步函數(shù)會(huì)開(kāi)啟子線程去執(zhí)行block1任務(wù)塊,block1中先打印任務(wù)2铺根,然后通過(guò)同步函數(shù)向并發(fā)隊(duì)列中添加block2任務(wù)塊宪躯,并發(fā)隊(duì)列不需要等前一個(gè)任務(wù)完成就可以安排下一個(gè)任務(wù)執(zhí)行,所以block2可以立馬執(zhí)行打印任務(wù)3位迂,最后再打印任務(wù)4访雪。
作為一個(gè)開(kāi)發(fā)者详瑞,有一個(gè)學(xué)習(xí)的氛圍跟一個(gè)交流圈子特別重要,這是一個(gè)我的iOS開(kāi)發(fā)交流群:130595548臣缀,不管你是大牛還是小白都?xì)g迎入駐 坝橡,讓我們一起進(jìn)步,共同發(fā)展>谩(群內(nèi)會(huì)免費(fèi)提供一些群主收藏的免費(fèi)學(xué)習(xí)書(shū)籍資料以及整理好的幾百道面試題和答案文檔F攀摹)