iOS與NSRunLoop

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/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咪笑,一起剝皮案震驚了整個濱河市可帽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌窗怒,老刑警劉巖映跟,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異扬虚,居然都是意外死亡努隙,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門辜昵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荸镊,“玉大人,你說我怎么就攤上這事堪置」妫” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵舀锨,是天一觀的道長岭洲。 經(jīng)常有香客問我,道長雁竞,這世上最難降的妖魔是什么钦椭? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮碑诉,結(jié)果婚禮上彪腔,老公的妹妹穿的比我還像新娘。我一直安慰自己进栽,他們只是感情好德挣,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著快毛,像睡著了一般格嗅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唠帝,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天屯掖,我揣著相機與錄音,去河邊找鬼襟衰。 笑死贴铜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瀑晒。 我是一名探鬼主播绍坝,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼苔悦!你這毒婦竟也來了轩褐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤玖详,失蹤者是張志新(化名)和其女友劉穎把介,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竹宋,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡劳澄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蜈七。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秒拔。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖飒硅,靈堂內(nèi)的尸體忽然破棺而出砂缩,到底是詐尸還是另有隱情,我是刑警寧澤三娩,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布庵芭,位于F島的核電站,受9級特大地震影響雀监,放射性物質(zhì)發(fā)生泄漏双吆。R本人自食惡果不足惜眨唬,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望好乐。 院中可真熱鬧匾竿,春花似錦、人聲如沸蔚万。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽反璃。三九已至昵慌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間淮蜈,已是汗流浹背斋攀。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留梧田,地道東北人蜻韭。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像柿扣,于是被迫代替她去往敵國和親肖方。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容