runloop:
我們的程序為什么運行起來后淤击,不手動終止運行的話酿秸,App會一直持續(xù)運行绷雏?NSRunLoop是App持續(xù)運行的保證。
Main函數(shù)中的RunLoop
我們看一下蝠检,如果沒有runloop:
// 沒有runloop循環(huán)沐鼠,啟動程序,打印出 Hello, World!后叹谁,程序馬上退出
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
}
return 0;
}
- 當(dāng)Main函數(shù)執(zhí)行到UIApplicationMain時饲梭,就開啟了RunLoop運行循環(huán)
- 在運行循環(huán)開啟時,就會保證程序的持續(xù)運行并且處理App的各種事件焰檩,不會退出
- Main函數(shù)中的RunLoop憔涉,被稱為主運行循環(huán),而主運行循環(huán)在整個App的生命周期中都不會被銷毀析苫,它是程序運行的保證
//程序在啟動時兜叨,第一步就會執(zhí)行main函數(shù),在main函數(shù)中會執(zhí)行以下操作
int main(int argc, char * argv[]) {
@autoreleasepool {
/*
*nil:UIApplication類名或者子類名衩侥,如果為nil国旷,就等于@"UIApplication"
*NSStringFromClass([AppDelegate class]):UIApplication代理的名稱
*/
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
程序啟動的完整流程
1.執(zhí)行main函數(shù)
2.執(zhí)行UIApplicationMain函數(shù)
1> 指定UIApplication對象
2> 指定UIApplication的代理
3.創(chuàng)建UIApplication對象,并且指定他的代理
4.創(chuàng)建一個事件循環(huán):主循環(huán)(RunLoop)茫死,并且是一個死循環(huán)跪但,保證程序的持續(xù)運行
5.加載配置了所有應(yīng)用程序信息的info.plist文件
1> 判斷 Main storyboard file base name 中有沒有指定 Main,即需要加載的 Storyboard 文件
2> 如果指定了就加載Main.storyboard文件
3> 如果沒有指定的話就會黑屏
6.應(yīng)用程序啟動完畢
RunLoops 是線程相關(guān)的基礎(chǔ)框架的一部分璧榄。一個runloop 就是一個事件處理循環(huán),用來不停地調(diào)度工作以及處理輸入等事件吧雹。
RunLoop 會再循環(huán)中處理App的各種事件骨杂,如 觸摸事件,定時器事件雄卷,selector事件
RunLoop最大的優(yōu)勢就是能節(jié)省CPU的資源搓蚪,提高程序的性能,它會在需要執(zhí)行任務(wù)的時候被喚醒丁鹉,沒有任務(wù)執(zhí)行的時候進(jìn)入休眠狀態(tài)
線程的生命周期存在五個狀態(tài):新建妒潭、就緒悴能、運行、阻塞雳灾、死亡
NSRunLoop 可以保持一個線程一直為活躍狀態(tài)漠酿,不會被馬上銷毀。
簡單應(yīng)用:
// 1.獲取主線程對應(yīng)的RunLoop對象
NSRunLoop *mainLoop = [NSRunLoop mainRunLoop];
// 2.獲取當(dāng)前線程對應(yīng)的RunLoop對象
NSRunLoop *currentLoop = [NSRunLoop currentRunLoop];
// 3.子線程中的RunLoop
[NSThread detachNewThreadSelector:@selector(threadTask) toTarget:self withObject:nil];
1.定時器:在主線程中設(shè)定一個一秒執(zhí)行一次的定時器谎亩,能確保它每一秒都會執(zhí)行一次嗎炒嘲?
答案是否定的,主線程中事件很多匈庭,如果有一個事件堵塞了0.5秒夫凸,那么定時器就就會延遲0.5秒。所以一般是專門開啟一個子線程運行添加定時器
- (void)threadTask {
/*子線程與RunLoop
1.每一個子線程阱持,都對應(yīng)一個自己的RunLoop
2.主線程的RunLoop在程序運行的時候就已經(jīng)開啟了夭拌,而子線程的RunLoop需要手動開啟
3.RunLoop需執(zhí)行run方法,來開啟衷咽,但如果RunLoop中沒有任何任務(wù)鸽扁,就會關(guān)閉
*/
//自動添加到RunLoop中
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(showTimer) userInfo:nil repeats:YES];
默認(rèn)加入了當(dāng)前RunLoop的NSDefaultRunLoopMode
模式
//需要手動添加到RunLoop中
NSTimer *timer2 = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(showTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer2 forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];
RunLoopMode :
default模式:幾乎包括所有輸入源(除NSConnection)
NSDefaultRunLoopMode
模式
mode模式:處理modal panels
connection模式:處理NSConnection事件,屬于系統(tǒng)內(nèi)部兵罢,用戶基本不用
event tracking模式:如組件拖動輸入源 UITrackingRunLoopModes
不處理定時事件
common modes模式:NSRunLoopCommonModes
這是一組可配置的通用模式献烦。將input sources與該模式關(guān)聯(lián)則同時也將input sources與該組中的其它模式進(jìn)行了關(guān)聯(lián)。
例如:
UIScrollView *scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
scrollView.backgroundColor = [UIColor orangeColor];
scrollView.contentSize = CGSizeMake(0, SCREEN_HEIGHT*3);
[self.view addSubview:scrollView];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(showTimer) userInfo:nil repeats:YES];
RunLoop一開始是NSDefaultRunLoopMode
模式卖词,在拖動scrollView的時候巩那,RunLoop變成UITrackingRunLoopModes
模式,這時定時器不再執(zhí)行此蜈,等到不再滑動即横,接著執(zhí)行。這時可以將模式改為 NSRunLoopCommonModes
,這樣在滑動的時候裆赵,定時器也是會執(zhí)行的
NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(showTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
參考
鏈接:http://www.reibang.com/p/ccf979198271
鏈接:http://www.reibang.com/p/c3a0a183142a
2018.7.19更新
scheduledTimerWithTimeInterval:
方法會自動添加到runloop
里东囚,但是主線程和子線程的情況不一樣:
- 1>主線程:主線程的
runloop
在程序啟動的時候就自動創(chuàng)建開啟了,定時器加到runloop
上就開始運行了战授; - 2>子線程:子線程的
runloop
是懶加載页藻,只有調(diào)用獲取runloop
的方法(CFRunLoopGetCurrent()、[NSRunLoop currentRunLoop])
植兰,才會創(chuàng)建runloop
份帐,普通的一次性的任務(wù)執(zhí)行結(jié)束后,線程就銷毀了,不需要用到runloop
楣导,不需要調(diào)用方法創(chuàng)建废境。當(dāng)一次任務(wù)執(zhí)行結(jié)束后,希望該線程不被銷毀,就需要開啟runloop
噩凹。開啟runloop
并正常運行需要滿足兩個條件:
(1)要有有內(nèi)容的mode(sourse或者timer)
;
(2)需要[[NSRunLoop currentRunLoop] run]
巴元。
當(dāng)runloop
里面沒有mode
,或者mode
里面沒有內(nèi)容驮宴,或者沒有調(diào)用run
方法逮刨,runloop
都不能正常開啟。所以在子線程中調(diào)用scheduledTimerWithTimeInterval:
就是創(chuàng)建runloop
并把定時器添加到runloop
上幻赚,這時還需要調(diào)用[[NSRunLoop currentRunLoop] run]
來開啟runloop
禀忆。
注:runloop
成功開啟后就進(jìn)入了循環(huán),開啟runloop
后的代碼就不會執(zhí)行了落恼。當(dāng)結(jié)束runloop
后(超出runloop
設(shè)定最大時間或手動停止)箩退,開始執(zhí)行后面的代碼。
何時使用 RunLoop
我們應(yīng)該只在創(chuàng)建子線程的時候,才顯示的運行一個 RunLoop
佳谦。 iOS app
會在應(yīng)用啟動的時候幫我run
一個 runloop
,而我們自己新建的子線程不會.
對于子線程戴涝,我們?nèi)匀恍枰袛嗍欠裥枰獑右粋€runloop
,比如我們使用一個線程去處理一個預(yù)先定義的長時間的任務(wù)钻蔑,我們應(yīng)該避免啟動runloop
啥刻。下面是官方document 提供的使用 RunLoop
的幾個場景:
? 1.需要使用 Port-Based Input Source或者 Custom InputSource 和其他thread通訊時
? 2.需要在線程中使用 Timer
? 3.需要在線程中使用selector相關(guān)的方法(performSelecter:afterDelay: 、 performSelector:onThread:等方法)
? 4.需要讓線程周期性的執(zhí)行某些工作
參考的文章:
runloop 入門:http://www.reibang.com/p/2d3c8e084205
runloop理解:http://www.reibang.com/p/f33c0e5ad0e2
runloop深入理解:https://blog.ibireme.com/2015/05/18/runloop/