寫出以下代碼段在控制臺的輸出, 并解釋為什么?
- (void)viewDidLoad {
? ? [super viewDidLoad];
? ? dispatch_async(dispatch_get_global_queue(0, 0), ^{
? ? ? ? NSLog(@"before perform");
? ? ? ? [self performSelector:@selector(printLog) withObject:nil afterDelay:0];
? ? ? ? NSLog(@"after perform");
? ? });
}
- (void)printLog
{
? ? NSLog(@"printLog");
}
答案: 只會輸出beform perform 以及 after perform
涉及技術(shù)棧: GCD全局隊(duì)列, performSelector實(shí)現(xiàn)原理以及 Runloop的了解
原理:?
1. GCD默認(rèn)的全局并發(fā)隊(duì)列, 在執(zhí)行任務(wù)的時候, 會從線程池獲取可執(zhí)行任務(wù)的線程, 如果沒有就阻塞(子線程)
2.performSelector的實(shí)現(xiàn)原理是設(shè)置一個timer到當(dāng)前的runloop中, 并且mode為NSDefaultRunLoopMode
3.子線程的runloop默認(rèn)不啟動
進(jìn)階答案: 如何加一行代碼使得printLog正常輸出
進(jìn)階解法: 1. 切入點(diǎn) 原理三: ?既然子線程的runloop默認(rèn)不啟動, 那么我們就在performSelector將timer加入currentRunloop中之后 [[NSRunLoop CurrentRunLoop] run]; 啟動子線程的runloop 就可以正常輸出了
- (void)viewDidLoad {
? ? [super viewDidLoad];
? ? dispatch_async(dispatch_get_global_queue(0, 0), ^{
? ? ? ? NSLog(@"before perform");
? ? ? ? [self performSelector:@selector(printLog) withObject:nil afterDelay:0];
? ? ? ? [[NSRunLoop currentRunLoop] run];
? ? ? ? NSLog(@"after perform");
? ? });
}
2. 思路二: 可以將performSelector放入主線程(此方法會影響printLog輸出順序)