****Runloop,顧名思義運(yùn)行循環(huán)的意思。在C語(yǔ)言中一段程序開(kāi)始于main函數(shù)中,當(dāng)main函數(shù)返回的時(shí)候一個(gè)程序就結(jié)束了。exit code 返回值。Runloop的存在,使我們可以進(jìn)行多線程編程的關(guān)鍵,控制線程生命周期并接收事件進(jìn)行處理。****
我們?cè)趍ain函數(shù)中寫(xiě)下代碼:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"start");
// 創(chuàng)建一個(gè)并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("testqueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue ,^{
NSLog(@"testqueue");
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"mainqueue");
});
NSLog(@"finished");
}
return 0;
}
Log如下:
2017-03-14 14:42:20.296801 GCDTest[1112:46964] start
2017-03-14 14:42:20.297515 GCDTest[1112:46964] finished
Program ended with exit code: 0
發(fā)現(xiàn)無(wú)論是異步回到主隊(duì)列,還是自己創(chuàng)建的并行隊(duì)列,都沒(méi)有執(zhí)行l(wèi)og。因?yàn)榇a是一行行向下執(zhí)行的,這里的代碼還沒(méi)有執(zhí)行異步操作時(shí),已經(jīng)return了,所以異步log并沒(méi)有打印出來(lái)。所以只要能讓main函數(shù)不走return就可以了。那么怎樣使main函數(shù)不return呢?我們往下看:
我們?cè)谶@段代碼后面插入一段runloop的代碼
// 讓當(dāng)前runloop(這里就是mainrunloop)開(kāi)始循環(huán)(run)
[[NSRunLoop currentRunLoop] run];
}
return 0;
運(yùn)行,輸出:
2017-03-14 14:53:33.001084 GCDTest[1206:51091] start
2017-03-14 14:53:33.001737 GCDTest[1206:51091] finished
2017-03-14 14:53:33.001763 GCDTest[1206:51118] testqueue
2017-03-14 14:53:33.002562 GCDTest[1206:51091] mainqueue
輸出了我們想要的結(jié)果,但是沒(méi)有exit 0;這說(shuō)明這段代碼一直沒(méi)有運(yùn)行完畢。這兒可能大家應(yīng)該會(huì)有疑問(wèn),既然ranloop開(kāi)始了循環(huán),為什么沒(méi)有一直循環(huán)下去,就像while(1){} 一樣。
這里就要講明一下runloop的運(yùn)行機(jī)制了:

上圖可以清晰的看出runloop在處理源(定時(shí)源、輸入源)事件時(shí)的運(yùn)行機(jī)制。依據(jù)上面的代碼分析,當(dāng)我們發(fā)起異步操作,打印log時(shí)都是在向當(dāng)前的runloop(子線程是子線程的runloop)插入源事件(entry),當(dāng)事件執(zhí)行完畢(exit),runloop就會(huì)掛起(Waiting for Activity)節(jié)省cpu資源,直到其他源插入,或者Timer源定時(shí)處理才被喚醒(wake up)。
開(kāi)發(fā)iOSapp的時(shí)候main函數(shù)中會(huì)自動(dòng)開(kāi)始運(yùn)行一個(gè)RunLoop(mainrunloop),所以app可以一直運(yùn)行,我們也不需要調(diào)用run函數(shù)。這段main函數(shù)的代碼保證了mainrunloop的運(yùn)行。
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
在子線程中使用runloop
runloop不能自己創(chuàng)建,但是每一個(gè)線程都有一個(gè)屬于自己的runloop,我們可以通過(guò)函數(shù)得到它,在iOS開(kāi)發(fā)中mainrunloop默認(rèn)是運(yùn)行狀態(tài)的,但是子線程的runloop則需要我們手動(dòng)開(kāi)啟。
我們創(chuàng)建一個(gè)Mythred的類(lèi)繼承NSthred,來(lái)觀察線程的生命周期
在dealloc中代碼:
-(void)dealloc
{
NSLog(@"%@:%s",NSStringFromClass([self class]),__FUNCTION__);
}
當(dāng)我們不啟動(dòng)線程的runloop,代碼:
// 創(chuàng)建一個(gè)線程
Mythred *thred = [[Mythred alloc] initWithBlock:^{
NSLog(@"start");
NSTimer *timer = [NSTimer timerWithTimeInterval:0.2 target:self selector:@selector(timerrun) userInfo:nil repeats:YES];
// 將timer源插入子線程的runloop中
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}];
// 啟動(dòng)線程
[thred start];
log:
2017-03-14 16:34:38.918 afafaenm[1878:84069] start
2017-03-14 16:34:38.919 afafaenm[1878:84069] Mythred:-[Mythred dealloc]
線程直接運(yùn)行完就被回收了。
啟動(dòng)子線程的runloop:
// 創(chuàng)建一個(gè)線程
Mythred *thred = [[Mythred alloc] initWithBlock:^{
NSLog(@"start");
NSTimer *timer = [NSTimer timerWithTimeInterval:0.2 target:self selector:@selector(timerrun) userInfo:nil repeats:YES];
// 將timer源插入子線程的runloop中
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
// 啟動(dòng)子線程的runloop
[[NSRunLoop currentRunLoop] run];
}];
// 啟動(dòng)線程
[thred start];
我們發(fā)現(xiàn)會(huì)一直輸出,這樣我們就可以是子線程的runloop一直處于循環(huán)運(yùn)行中,所以這個(gè)子線程就不會(huì)被回收,會(huì)一直log輸出。
我們還可以設(shè)置runloop運(yùn)行時(shí)間
將代碼替換,
// [[NSRunLoop currentRunLoop] run];
// 2秒鐘之后就不在輸出,線程就會(huì)被回收
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
log:
2017-03-14 16:40:01.693 afafaenm[1931:86342] start
2017-03-14 16:40:01.953 afafaenm[1931:86342] -[ViewController timerrun]
2017-03-14 16:40:02.168 afafaenm[1931:86342] -[ViewController timerrun]
2017-03-14 16:40:02.346 afafaenm[1931:86342] -[ViewController timerrun]
2017-03-14 16:40:02.495 afafaenm[1931:86342] -[ViewController timerrun]
2017-03-14 16:40:02.764 afafaenm[1931:86342] -[ViewController timerrun]
2017-03-14 16:40:02.948 afafaenm[1931:86342] -[ViewController timerrun]
2017-03-14 16:40:03.161 afafaenm[1931:86342] -[ViewController timerrun]
2017-03-14 16:40:03.348 afafaenm[1931:86342] -[ViewController timerrun]
2017-03-14 16:40:03.561 afafaenm[1931:86342] -[ViewController timerrun]
2017-03-14 16:40:03.694 afafaenm[1931:86342] Mythred:-[Mythred dealloc]
可以看出2秒后timer不再輸出,線程被回收。
另外需要注意,Runloop并不是線程安全的。