iOS多線程-RunLoop簡(jiǎn)介

什么是RunLoop?
從字面上來看是運(yùn)行循環(huán)的意思.

內(nèi)部就是一個(gè)do{}while循環(huán),在這個(gè)循環(huán)里內(nèi)部不斷的處理各種任務(wù)(比如:source/timer/Observer)

RunLoop的存在其實(shí)就是為線程而存在的.線程的作用就是執(zhí)行一個(gè)特定的任務(wù),但是默認(rèn)情況下線程執(zhí)行完任務(wù)后就不能再次執(zhí)行任務(wù),這是因?yàn)槟J(rèn)情況下線程是沒有開啟RunLoop的.如果開啟RunLoop之后,線程執(zhí)行完任務(wù)之后,會(huì)一直等待,直到再次接受到任務(wù),接續(xù)執(zhí)行任務(wù).線程銷毀前,會(huì)先釋放這個(gè)線程所對(duì)應(yīng)的RunLoop.

RunLoop基本作用
保持程序的持續(xù)運(yùn)行,保持線程的持續(xù)運(yùn)行.

處理App中的各種事件(比如觸摸事件,定時(shí)器事件,Selector事件)

節(jié)省CPU資源,提高程序性能:該做事時(shí)做事,該休息時(shí)休息

RunLoop對(duì)象
ios中有2套API來訪問和使用RunLoop

一套是Fundation(純OC的)框架中的
NSRunLoop
// 獲得當(dāng)前線程的RunLoop對(duì)象[NSRunLoop currentRunLoop];// 獲得主線程的RunLoop對(duì)象[NSRunLoop mainRunLoop];

一套是Core Fundation(純C語言的)框架中的
CFRunLoopRef
// 獲得當(dāng)前線程的RunLoop對(duì)象CFRunLoopGetCurrent();// 獲得主線程的RunLoop對(duì)象CFRunLoopGetMain();

NSRunLoo和CFRunLoopRef都代表著RunLoop對(duì)象.NSRunLoop是基于CFRunLoopRef的一層OC包裝

RunLoop與線程
每條線程都有唯一的一個(gè)與之對(duì)應(yīng)的RunLoop對(duì)象

主線程的Runloop系統(tǒng)已經(jīng)自動(dòng)創(chuàng)建好了,子線程的RunLoop需要手動(dòng)創(chuàng)建

RunLoop在第一次獲取時(shí)由系統(tǒng)自動(dòng)創(chuàng)建,在線程結(jié)束時(shí)銷毀

如果想給子線程創(chuàng)建RunLoop,不能直接alloc&init,只要調(diào)用獲取當(dāng)前線程RunLoop方法即可,系統(tǒng)會(huì)自動(dòng)放回當(dāng)前線程的RunLoop,如果當(dāng)前線程沒有RunLoop,系統(tǒng)會(huì)自動(dòng)創(chuàng)建.

RunLoop相關(guān)類

Core Fundation中關(guān)于RunLoop的5個(gè)類
CFRunLoopRef: RunLoop對(duì)象

CFRunLoopModeRef: RunLoop運(yùn)行模式.

CFRunLoopSoruceRef: 事件源(輸入源)

CFRunLoopTimerRef:基于時(shí)間的觸發(fā)器.

CFRunLoopObserverRef: 觀察者,能夠監(jiān)聽RunLoop的狀態(tài)改變

CFRunLoopModeRef

CFRunLoopModeRef代表RunLoop運(yùn)行模式

一個(gè)RunLoop對(duì)象包含若干個(gè)Mode(模式),每個(gè)Mode又包含若干個(gè) source/Timer/Observer

RunLoop運(yùn)行時(shí),只能指定一個(gè)Mode, 這個(gè)Mode又稱之為CurrentMode,然后RunLoo就執(zhí)行CurrentMode中的source/Timer/Observer

如果需要切換Mode,只能退出RunLoop,再重新指定一個(gè)Mode進(jìn)入,這樣做是為了分隔開不同組的Source/Timer/Observer,讓其不受影響

系統(tǒng)默認(rèn)注冊(cè)了5個(gè)Mode:
NSDefaultRunLoopMode: App的默認(rèn)Mode,通常主線程實(shí)在這個(gè)模式下運(yùn)行

UITrackingRunLoopMode:界面跟蹤Mode,用于界面控件(ScrollView,tableView等等)追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他Mode影響

UIInitializationRunLoopMode:在剛啟動(dòng)App是進(jìn)入的第一個(gè)Mode,啟動(dòng)完成后就不再使用

GSEventReceiveRunLoopMode:接收系統(tǒng)事件的內(nèi)部Mode,通常用不到

NSRunLoopCommonMode:這是一個(gè)占位的Mode,不是一種真正的Mode,(可以看成模式組,默認(rèn)情況下包括了NSDefaultRunLoopMode,UITrackingRunLoopMode)兩種模式.

CFRunLoopTimerRef
CFRunLoopTimerRef是基于時(shí)間的觸發(fā)器

CFRunLoopTimerRef基本上說的就是NSTimer,它受RunLoop的Mode影響

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //創(chuàng)建一個(gè)NSTimer定時(shí)器,默認(rèn)情況下NSTimer是不會(huì)執(zhí)行的,只有把NSTimer添加到RunLoop中,由RunLoop管理執(zhí)行
    NSTimer * timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(show) userInfo:nil repeats:YES];
    // 在當(dāng)前線程中RunLoop添加一個(gè)timer, 并告訴runLoop, 這個(gè)timer只能在NSDefaultRunLoopMode模式下才能觸發(fā)
    // runLoop會(huì)找到NSDefaultRunLoopMode,然后把timer添加NSDefaultRunLoopMode中的Timer數(shù)組中
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    //在當(dāng)前線程中RunLoop添加一個(gè)timer, 并告訴runLoop, 這個(gè)timer只能在NSRunLoopCommonModes模式下才能觸發(fā)
    //runLoop會(huì)找到NSDefaultRunLoopMode和UITrackingRunLoopMode
    //然后把timer添加NSDefaultRunLoopMode和UITrackingRunLoopMode中的Timer數(shù)組中
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    //利用此方法創(chuàng)建的NSTimer, 系統(tǒng)會(huì)自動(dòng)放入當(dāng)前線程中的currentRunLoop中,并且只能在NSDefaultRunLoop模式下才能觸發(fā)
    NSTimer * timer1 = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
    //雖然通過類方法scheduledTimerWithTimeInterval創(chuàng)建NSTimer,會(huì)自動(dòng)添加到NSDefaultRunLoopMode模式中
    //但我們還是可以修改它的模式
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

CFRunLoopSoruceRef
按照官方文檔,source的分類:
Port-Based Sources:基于端口的事件源:監(jiān)聽程序響應(yīng)的端口,基于端口事件是由系統(tǒng)內(nèi)核自動(dòng)發(fā)送的.

Custom Input Sources: 自定義輸入源:監(jiān)聽自定義事件源,而自定義的輸入源是需要人工從其他線程發(fā)送

Cocoa Perfrom Selector Source: selector事件源

按照源碼函數(shù)調(diào)用棧,source的分類:
Source0:非基于Prot(端口)的,是用戶主動(dòng)觸發(fā)的事件

Source1:基于Prot(端口)的,通過內(nèi)核和其他線程相互發(fā)送消息

CFRunLoopObserverRef
CFRunLoopObserverRef:觀察者對(duì)象,可以監(jiān)聽RunLoop的狀態(tài)

RunLoop狀態(tài):
kCFRunLoopEntry 即將進(jìn)入runLoop

kCFRunLoopBeforeTimers 即將處理Timer

kCFRunLoopBeforeSources 即將處理source(事件源)

kCFRunLoopBeforeWaiting 即將進(jìn)入休眠

kCFRunLoopAfterWaiting 即將從休眠中醒來

kCFRunLoopExit 即將退出runLoop

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ 
    //創(chuàng)建一個(gè)CFRunLoopObserverRef
    /*第一個(gè)參數(shù): CFRunLoopObserverRef(觀察者)分配內(nèi)存空間方式第二個(gè)參數(shù): 監(jiān)聽那些狀態(tài) kCFRunLoopAllActivities(監(jiān)聽所有狀態(tài))第三個(gè)參數(shù): 是否每次都要監(jiān)聽第四個(gè)參數(shù): 優(yōu)先級(jí)第五個(gè)參數(shù): 回調(diào)函數(shù)*/
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES,0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { 
    // observer監(jiān)聽對(duì)象 
    //activity Runloop當(dāng)前狀態(tài)
    });
    /*第一個(gè)參數(shù): 為那個(gè)線程下的RunLoop添加CFRunLoopObserverRef(觀察者)第二個(gè)參數(shù): 需要添加的CFRunLoopObserverRef(觀察者)第三個(gè)參數(shù): 把監(jiān)聽添加到RunLoop那個(gè)模式中
    */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    //記得內(nèi)存管理,因?yàn)镃ore Foundation不在ARC管理范圍內(nèi)
    //帶有Create、Copy氏身、Retain等字眼的函數(shù)巍棱,創(chuàng)建出來的對(duì)象,都需要在最后做一次release
    //銷毀對(duì)象函數(shù):CFRelease對(duì)象CFRelease(observer);
}

RunLoop處理邏輯

如果RunLoop中沒有Timer或source,RunLoop就會(huì)立刻退出

每次運(yùn)行RunLoop,RunLoop會(huì)自動(dòng)處理之前未處理的消息,并通知相關(guān)觀察者.具體順序如下:
1.通知觀察者RunLoop已經(jīng)啟動(dòng)

2.通知觀察者即將開始啟動(dòng)定時(shí)器

3.通知觀察者即將啟動(dòng)非基于端口的事件源

4.啟動(dòng)任何準(zhǔn)備好的非基于端口的事件源

5.如果基于端口的事件源準(zhǔn)備好并處于等待得狀態(tài),立即啟動(dòng).并進(jìn)入步驟9

6.通知觀察者線程進(jìn)入休眠

7.將線程置于休眠直到任意下面的事件發(fā)生:
某一事件到達(dá)基于端口的源

定時(shí)器啟動(dòng)

RunLoop設(shè)置的時(shí)間已經(jīng)超時(shí).(系統(tǒng)底層會(huì)給RunLoop設(shè)置一個(gè)超時(shí)時(shí)間,源碼中設(shè)置的是:9999999999.0)

RunLoop被手動(dòng)喚醒

8.通知觀察者線程將被喚醒.

9.處理未處理的事件
如果用戶定義的定時(shí)器啟動(dòng),處理定時(shí)器事件并重啟RunLoop.進(jìn)入步驟2

如果事件源啟動(dòng),傳遞相應(yīng)的消息

如果RunLooop被顯示喚醒而且時(shí)間還沒超時(shí),重啟RunLoop.進(jìn)入步驟2

10.通知觀察者RunLoop結(jié)束.

如何讓子線程成為常駐線程(讓一個(gè)子線程不進(jìn)入消亡狀態(tài)蛋欣,等待其他線程發(fā)來消息航徙,處理其他事件)

(#)import "ViewControllerRunLoop.h"
@interface ViewControllerRunLoop ()
@property(nonatomic,strong)NSThread * thread;
@end
@implementation ViewControllerRunLoop
-(void)viewDidLoad { 
    //創(chuàng)建子線程執(zhí)行任務(wù) self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];    [self.thread start];
}
-(void)run { 
    NSLog(@"跑起來"); 
    //默認(rèn)情況下,子線程是不會(huì)常駐的 
    //只有子線程中runloop啟動(dòng),并且runloop中有source或timer,才會(huì)常駐 
    //只有常駐線程才能再次執(zhí)行任務(wù),因?yàn)榫€程中有runloop來處理事件了 
    //子線程的runloop是需要手動(dòng)創(chuàng)建的, 并且需要手動(dòng)啟動(dòng) 
    NSRunLoop * rl = [NSRunLoop currentRunLoop]; 
    //如果子線程的runloop沒有 source / timer 的話, 哪么子線程的runloop會(huì)立即關(guān)閉 
    //在runLoop中添加一個(gè)timer 
    [NSTimer scheduledTimerWithTimeInterval:2 target:self selector: @selector(timerRun) userInfo:nil repeats:YES]; 
    //啟動(dòng)runloop [rl run]; 
    //如果線程成為了常駐線程,你會(huì)發(fā)現(xiàn),不會(huì)執(zhí)行到這行代碼 
    //也就是說這個(gè)方法不會(huì)執(zhí)行完, 
    NSLog(@"end");
}
-(void)timerRun{ 
    NSLog(@"%s",__func__);
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ 
    // 讓子線程再次執(zhí)行任務(wù) 
    [self performSelector:@selector(againRun) onThread:self.thread withObject:nil waitUntilDone:NO];
}
-(void)againRun{ 
    NSLog(@"再次跑起來");
}
@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市陷虎,隨后出現(xiàn)的幾起案子到踏,更是在濱河造成了極大的恐慌,老刑警劉巖尚猿,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窝稿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡凿掂,警方通過查閱死者的電腦和手機(jī)伴榔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來庄萎,“玉大人踪少,你說我怎么就攤上這事〔夜В” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵耙旦,是天一觀的道長(zhǎng)脱羡。 經(jīng)常有香客問我,道長(zhǎng)免都,這世上最難降的妖魔是什么锉罐? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮绕娘,結(jié)果婚禮上脓规,老公的妹妹穿的比我還像新娘。我一直安慰自己险领,他們只是感情好侨舆,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绢陌,像睡著了一般挨下。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脐湾,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天臭笆,我揣著相機(jī)與錄音,去河邊找鬼。 笑死愁铺,一個(gè)胖子當(dāng)著我的面吹牛鹰霍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茵乱,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼茂洒,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了似将?” 一聲冷哼從身側(cè)響起获黔,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎在验,沒想到半個(gè)月后玷氏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腋舌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年盏触,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片块饺。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赞辩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出授艰,到底是詐尸還是另有隱情辨嗽,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布淮腾,位于F島的核電站糟需,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏谷朝。R本人自食惡果不足惜洲押,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望圆凰。 院中可真熱鬧杈帐,春花似錦、人聲如沸专钉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)跃须。三九已至炮沐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間回怜,已是汗流浹背大年。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工换薄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人翔试。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓轻要,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親垦缅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子冲泥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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

  • 什么是RunLoop?從字面上來看是運(yùn)行循環(huán)的意思.內(nèi)部就是一個(gè)do{}while循環(huán),在這個(gè)循環(huán)里內(nèi)部不斷的處理...
    deve_雨軒閱讀 29,500評(píng)論 1 32
  • Run loop 剖析:Runloop 接收的輸入事件來自兩種不同的源:輸入源(intput source)和定時(shí)...
    Mitchell閱讀 12,411評(píng)論 17 111
  • 前言 最近離職了,可以盡情熬夜寫點(diǎn)總結(jié),不用擔(dān)心第二天上班爽并蛋疼著壁涎,這篇的主角 RunLoop 一座大山凡恍,涵蓋的...
    zerocc2014閱讀 12,367評(píng)論 13 67
  • 先貼下 apple doc, 本文基本是對(duì)照 doc 的翻譯:https://developer.apple.co...
    brownfeng閱讀 6,849評(píng)論 8 111
  • 1 RunLoop簡(jiǎn)介 神秘的RunLoop。一個(gè)應(yīng)用開始運(yùn)行以后放在那里怔球,如果不對(duì)它進(jìn)行任何操作嚼酝,這個(gè)應(yīng)用就像靜...
    Claire_wu閱讀 1,760評(píng)論 3 30