本人小白,歡迎各位大佬補(bǔ)充指點(diǎn)
先說說我是咋理解runloop的吧?然后在專業(yè)的講講
本人理解:
- 從C語言的角度來看,整個(gè)app就是調(diào)用了一個(gè)main函數(shù),只是這個(gè)main函數(shù)一直不返回值.
- main函數(shù)內(nèi)部:
常識(shí):執(zhí)行任務(wù)需要開啟一個(gè)線程,因?yàn)槿蝿?wù)必須在線程下執(zhí)行
一般的main函數(shù)執(zhí)行流程:開啟一個(gè)線程->添加許多任務(wù)(即寫了很多代碼)->任務(wù)完成(代碼執(zhí)行完畢)->線程關(guān)閉->返回main函數(shù)值程序結(jié)束 - runloop啥作用?
Runloop就是一個(gè)死循環(huán),把這個(gè)死循環(huán)放在上面開啟的線程中,那么線程就永遠(yuǎn)不會(huì)結(jié)束.因此main永遠(yuǎn)不返回.但是這個(gè)死循環(huán)比較牛逼,下面講講他的牛逼之處:
一般死循環(huán)特點(diǎn):
1>程序一旦進(jìn)入死循環(huán),一直循環(huán)執(zhí)行,永不停歇,并且執(zhí)行速度非???br> 2>只執(zhí)行這段代碼,其他任何代碼都不會(huì)執(zhí)行,也就是說,只專注在這跑圈,不管外界發(fā)生任何事
Runloop特點(diǎn):
程序一旦進(jìn)入runloop,當(dāng)外界沒事的時(shí)候,他就慢慢跑圈,(至于多慢我就不知道了),甚至休眠,一旦有事,他就快速跑到該事件,去執(zhí)行該事件,執(zhí)行完畢后再接著休眠
下面專業(yè)系統(tǒng)的講一下:
RunLoop的傳說
- 什么是runloop?
字面理解:運(yùn)行循環(huán)/跑圈 - 基本作用
1>保持程序的持續(xù)運(yùn)行
打開app它一直不死,就是因?yàn)閞unloop
2>處理App中的各種事件(比如觸摸事件、定時(shí)器事件、Selector事件)
3>節(jié)省CPU資源,提高程序性能:該做事時(shí)做事,該休息時(shí)休息
能讓我們的主線程,有事情就做事情,沒事情就休息,比如點(diǎn)擊事件:點(diǎn)擊前主線程在休息,當(dāng)點(diǎn)擊事件處理完畢后,讓主線程再次休息… - 如果沒有runloop
int main(int argc, char * argv[]) {
NSLog(@"execute main function");
return 0;
}
第3行后程序就結(jié)束了
如果有了runloop大致運(yùn)行代碼邏輯如下:

由于main函數(shù)里面( UIApplicationMain())啟動(dòng)了個(gè)RunLoop,所以程序并不會(huì)馬上退出,保持持續(xù)運(yùn)行狀態(tài)
main函數(shù)中的RunLoop
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
代碼的UIApplicationMain函數(shù)內(nèi)部就啟動(dòng)了一個(gè)RunLoop
所以UIApplicationMain函數(shù)一直沒有返回,保持了程序的持續(xù)運(yùn)行
這個(gè)默認(rèn)啟動(dòng)的RunLoop是跟主線程相關(guān)聯(lián)的
既然runloop那么牛逼,那么我們就來認(rèn)識(shí)他一下:
RunLoop對象
iOS中有2套API來訪問和使用RunLoop:
Foundation(OC):NSRunLoop
Core Foundation(C):CFRunLoopRef
該框架有個(gè)特點(diǎn):所有的類都已CF開頭,以Ref(reference:引用)結(jié)尾
NSRunLoop和CFRunLoopRef都代表著RunLoop對象
NSRunLoop是基于CFRunLoopRef的一層OC包裝,所以要了解RunLoop內(nèi)部結(jié)構(gòu),需要多研究CFRunLoopRef層面的API(Core Foundation層面)
RunLoop與線程關(guān)系
上面說到,runloop通常是用來卡住線程的,那么他們必然有關(guān)系啦
- 每條線程都有唯一的一個(gè)與之對應(yīng)的RunLoop對象
主線程的RunLoop已經(jīng)自動(dòng)創(chuàng)建好了,并且主動(dòng)啟動(dòng)了,子線程的RunLoop需要主動(dòng)創(chuàng)建,然后手動(dòng)啟動(dòng) - RunLoop在第一次獲取時(shí)創(chuàng)建,在線程結(jié)束時(shí)銷毀
獲得RunLoop對象
- Foundation
[NSRunLoop currentRunLoop]; // 獲得當(dāng)前線程的RunLoop對象
[NSRunLoop mainRunLoop]; // 獲得主線程的RunLoop對象 - Core Foundation
CFRunLoopGetCurrent(); // 獲得當(dāng)前線程的RunLoop對象
CFRunLoopGetMain(); // 獲得主線程的RunLoop對象
上面講了3條理論,下面代碼證明一下:
- (void)viewDidLoad {
[super viewDidLoad];
//當(dāng)前線程就是主線程
NSLog(@"%p--%p",[NSRunLoop mainRunLoop],[NSRunLoop currentRunLoop]);
//創(chuàng)建一個(gè)線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];
}
-(void)run{
/*
[NSRunLoop currentRunLoop]:這就是在子線程中創(chuàng)建了一個(gè)runloop,不是通過alloc/init,而且該創(chuàng)建是懶加載的
通過分析runloop(C語言)源碼過程如下:
創(chuàng)建runloop函數(shù)只有一個(gè)參數(shù)t:線程(這也說明一個(gè)runloop只有一個(gè)線程)
_CFRunLoopGet0(pthread_t t) {
1.如果t==kNilPthreadT(0的意思),那么t就等于主線程
__CFRunLoops:本身是一個(gè)可變字典,用于存儲(chǔ)所有的runloop,key值為線程,value為runloop
__CFRunLoops :{
mainthread : mainloop,
thread1:loop1,
...
}
2.如果發(fā)現(xiàn)__CFRunLoops為空值
2.1先創(chuàng)一個(gè)可變字典(就是__CFRunLoops)
2.2根據(jù)主線程創(chuàng)建一個(gè)主runloop,即mainloop
2.3將mainloop存入可變字典,key值為主線程
3.根據(jù)傳進(jìn)來的線程到字典中拿到相應(yīng)的runloop
4.沒有創(chuàng)建一個(gè)newloop->將newloop存入可變字典->返回這個(gè)newloop
5.有就直接返回loop
}
*/
NSLog(@"%p",[NSRunLoop currentRunLoop]);
}
RunLoop的相關(guān)類
學(xué)到對象,那么當(dāng)然就有學(xué)到一些該對象相關(guān)的類了
Core Foundation中關(guān)于RunLoop的5個(gè)類
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
這幾個(gè)類的關(guān)系圖如下:

分析:
RunLoop就像一個(gè)汽車,要跑起來,那么就要裝一些必備的東西
RunLoop里面有很多Mode對象->每個(gè)Mode對象里面又有三種集合分別存放:Source/Observer/Timer對象->有了這些Mode以及子內(nèi)容runloop才能一直跑圈/休息/有事件觸發(fā)
注意:runloop中的Mode中必須有source或者timer才能一直運(yùn)行,否則跑一圈就停了
下面分別講一講這幾個(gè)類:
CFRunLoopModeRef
- CFRunLoopModeRef代表RunLoop的運(yùn)行模式
- 一個(gè) RunLoop 包含若干個(gè) Mode,每個(gè)Mode又包含若干個(gè)Source/Timer/Observer
- 每次RunLoop啟動(dòng)時(shí),只能指定其中一個(gè) Mode,這個(gè)Mode被稱作 CurrentMode
[NSRunLoop currentRunLoop] runMode: beforeDate: - 如果需要切換Mode,只能退出Loop,再重新指定一個(gè)Mode進(jìn)入
這樣做主要是為了分隔開不同組的Source/Timer/Observer,讓其互不影響 -
系統(tǒng)默認(rèn)注冊了5個(gè)Mode:(前兩種最重要)
kCFRunLoopDefaultMode: App的默認(rèn)Mode,通常主線程是在這個(gè)Mode下運(yùn)行
UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響
分析:當(dāng)我們滾動(dòng)圖片時(shí)就會(huì)切換到這個(gè)模式,跟蹤你的滾動(dòng),一旦停止?jié)L動(dòng)就會(huì)切換到Default模式
舉例:當(dāng)我們滾動(dòng)的時(shí)候,其他事件沒反應(yīng),比如定時(shí)器,當(dāng)滾動(dòng)的時(shí)候定時(shí)器沒反應(yīng),因?yàn)樗贒efault模式中
UIInitializationRunLoopMode: 在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode,啟動(dòng)完成后就不再使用
GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到
kCFRunLoopCommonModes: 這是一個(gè)占位用的Mode,不是一種真正的Mode
CFRunLoopTimerRef
- CFRunLoopTimerRef是基于時(shí)間的觸發(fā)器,基本上說的就是NSTimer
- 它受runloop的Mode影響
- GCD的定時(shí)器不受runLoop的Mode影響
GCD定時(shí)器實(shí)現(xiàn)不一樣
代碼舉例:
- (void)viewDidLoad {
[super viewDidLoad];
//向sb中拖入一個(gè)textView
//情況一:沒有滾動(dòng)textView時(shí),1秒打印一次,一旦滾動(dòng)textView,定時(shí)器無用了,停止?jié)L動(dòng),定時(shí)器有起作用了
// [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
//上面一段代碼做了很多事情,等價(jià)代碼如下:
//timer添加到defaultMode里面去,mode再添加到runloop中去,runloop在啟動(dòng)時(shí)候再指定這個(gè)mode->拿出timer來用
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
//也就說只能在Defaultmode下好使
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
//情況二:如果想要一滾動(dòng)timer就有用,一停止?jié)L動(dòng)timer就失效,那么模式就用UITrackingRunLoopMode
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
//情況三:滾動(dòng)不影響定時(shí)器
//定時(shí)器只會(huì)跑在標(biāo)記為CommonModes的模式下(或者用GCD定時(shí)器)
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//通過打印runloop,我們發(fā)現(xiàn)kCFRunLoopDefaultMode/UITrackingRunLoopMode這兩種模式都被標(biāo)記為了commonmodes標(biāo)簽了,因此就可以做到了
NSLog(@"%@",[NSRunLoop currentRunLoop]);
}
- (void)test {
NSLog(@"----");
}
CFRunLoopSourceRef
- 系統(tǒng)定的我們用不著
- CFRunLoopSourceRef是事件源(輸入源)
- 我們的一些觸發(fā)事件,都是由source觸發(fā)的
1>以前的分法
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources(處理performselector調(diào)用)
2>根據(jù)函數(shù)調(diào)用棧分法
Source0:非基于Port
Source1:基于Port的,通過內(nèi)核和其他線程通信,接收/分發(fā)系統(tǒng)事件
CFRunLoopObserverRef
- CFRunLoopObserverRef是觀察者,能夠監(jiān)聽RunLoop的狀態(tài)改變
可以監(jiān)聽的時(shí)間點(diǎn)有以下幾個(gè):
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),//即將進(jìn)入loop,左移動(dòng)0->2的0次放= 1
kCFRunLoopBeforeTimers = (1UL << 1),//即將處理Timer …2
kCFRunLoopBeforeSources = (1UL << 2),//即將處理source...4
kCFRunLoopBeforeWaiting = (1UL << 5),//即將進(jìn)入休眠 …32
kCFRunLoopAfterWaiting = (1UL << 6),//剛剛從休眠中喚醒...64
kCFRunLoopExit = (1UL << 7),//即將推出loop 128
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
代碼舉例
// 創(chuàng)建observer
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"----監(jiān)聽到RunLoop狀態(tài)發(fā)生改變---%zd", activity);
});
//添加觀察者到當(dāng)前線程的RunLoop的當(dāng)前Mode中,監(jiān)聽runloop的狀態(tài)
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
// 釋放Observer
CFRelease(observer);
/*
//需要自己管理內(nèi)存
CF的內(nèi)存管理(Core Foundation)
1.凡是帶有Create、Copy、Retain等字眼的函數(shù),創(chuàng)建出來的對象,都需要在最后做一次release
* 比如CFRunLoopObserverCreate
2.release函數(shù):CFRelease(對象);
*/
RunLoop的邏輯流程圖

RunLoop的應(yīng)用
- performSelector的使用
//延遲3秒設(shè)置圖片并且值只在NSDefaultRunLoopMode模式下,也就是說,在滾動(dòng)的時(shí)候,不顯示圖片,滾動(dòng)停止,顯示圖片
[self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"123"] afterDelay:3.0 inModes:@[NSDefaultRunLoopMode]
- 常駐線程
#import "ViewController.h"
#import "ZHThread.h"
@interface ViewController ()
@property (nonatomic,strong) ZHThread *thread;
@end
@implementation ViewController
/*
常駐線程:讓一個(gè)線程永遠(yuǎn)不死
通過繼承NSThread并且從寫delloc監(jiān)聽線程,可以看到,開啟線程后執(zhí)行完任務(wù)立馬死掉
注意:不能簡單搞個(gè)屬性強(qiáng)引用,因?yàn)榫€程一旦執(zhí)行完畢,即使不釋放掉,當(dāng)前的線程也處于消亡狀態(tài),從新開啟([self.thread start])會(huì)崩潰
當(dāng)然不通過start調(diào)用,通過performselector調(diào)用雖然不會(huì)崩潰,但是不會(huì)調(diào)用run2,因?yàn)榫€程處于消亡狀態(tài)
//解決辦法:線程中添加runloop
好處:可以隨時(shí)讓拿到這個(gè)線程讓他做一些事情.沒有做事情之前,這個(gè)runloop處于休眠狀態(tài),一旦調(diào)用該線程處理事件,就回喚醒runloop去執(zhí)行該線程的事件
使用:搞一個(gè)常駐線程監(jiān)控聯(lián)網(wǎng)狀態(tài)
原理:
1.創(chuàng)建一個(gè)子線程并開啟線程
2.在開啟方法(run)中添加一個(gè)runloop,并開啟runloop
3.runloop會(huì)一直跑圈不會(huì)執(zhí)行完畢,阻止了開啟方法的執(zhí)行完畢即卡住子線程不讓他執(zhí)行完畢
4.一旦子線程中有事件,runloop會(huì)被喚醒,讓改事件在該線程中執(zhí)行,一旦執(zhí)行完畢,繼續(xù)跑圈卡住該線程
如果用一個(gè)死循環(huán)代替runloop是不可以的,因?yàn)樗姥h(huán)一直在處理這個(gè)事件,不會(huì)停下來先去執(zhí)行觸發(fā)事件
*/
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[ZHThread alloc] initWithTarget:self selector:@selector(run) object:nil];
//執(zhí)行線程
[self.thread start];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];
//隨時(shí)拿到這個(gè)子線程執(zhí)行事件
[self performSelector:@selector(run2) onThread:self.thread withObject:nil waitUntilDone:nil];
}
-(void)run{
NSLog(@"=====run====");
//run:默認(rèn)為NSDefaultRunLoopMode模式,時(shí)間是永遠(yuǎn),下面三行代碼等價(jià)
//往里面添加source(mode中啥都沒有,runloop沒用)
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
//啟動(dòng)runloop
[[NSRunLoop currentRunLoop] run];
// [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
// [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
NSLog(@"永遠(yuǎn)不會(huì)調(diào)用======");
}
-(void)run2{
NSLog(@"=====run2====");
}
上面代碼用圖解如下:

- 在子線程搞個(gè)定時(shí)器一直調(diào)用
//創(chuàng)建子線程初始化調(diào)用這個(gè)方法(將上面viewdidload方法中run改為runx即可)
-(void)runx{
@autoreleasepool {
//創(chuàng)建定時(shí)器
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run2) userInfo:nil repeats:YES];
//將定時(shí)器添加到runloop
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
//或者
// [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run2) userInfo:nil repeats:YES];
//放在主線程中就不會(huì)用到這句,因?yàn)橹骶€程一直在run,不用手動(dòng)run
// [[NSRunLoop currentRunLoop] run];
}
}
- 自動(dòng)釋放池
- 將所有對象都放到這個(gè)池子中去,到池子釋放的時(shí)候給池子中的每一個(gè)對象release
- 自動(dòng)釋放池什么時(shí)候釋放?(看源碼得到)
runloop休眠之前會(huì)釋放一次,因?yàn)樾菝呖赡軙?huì)很長,如果不釋放,會(huì)堆積很多
釋放后會(huì)同時(shí)創(chuàng)建一個(gè)新的釋放池,處理新一輪事件
因此,在創(chuàng)建runloop的代碼外層最好包裝一層自動(dòng)釋放池
比如上面的代碼
GCD的定時(shí)器使用
- GCD的定時(shí)器不受runLoop的Mode影響(滾動(dòng)界面時(shí),定時(shí)器不受影響)
GCD定時(shí)器實(shí)現(xiàn)不一樣 - GCD比NSTimer準(zhǔn)確
代碼舉例:
#import "ViewController.h"
@interface ViewController ()
/** 定時(shí)器(這里不用帶*,因?yàn)閐ispatch_source_t就是個(gè)類,內(nèi)部已經(jīng)包含了*) */
@property (nonatomic, strong) dispatch_source_t timer;
@end
@implementation ViewController
int count = 0;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 獲得隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
// 創(chuàng)建一個(gè)定時(shí)器(dispatch_source_t本質(zhì)還是個(gè)OC對象)
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 設(shè)置定時(shí)器的各種屬性(幾時(shí)開始任務(wù),每隔多長時(shí)間執(zhí)行一次)
// GCD的時(shí)間參數(shù),一般是納秒(1秒 == 10的9次方納秒)NSEC_PER_SEC = 1s
// dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC) 比當(dāng)前時(shí)間晚3秒
//當(dāng)前時(shí)間直接寫:DISPATCH_TIME_NOW
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);
dispatch_source_set_timer(self.timer, start, interval, 0);
// 設(shè)置回調(diào)
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"------------%@", [NSThread currentThread]);
count++;
if (count == 4) {
// 取消定時(shí)器
dispatch_cancel(self.timer);
self.timer = nil;
}
});
// 啟動(dòng)定時(shí)器
dispatch_resume(self.timer);
}
@end
IOS程序啟動(dòng)與運(yùn)轉(zhuǎn)
注:這段是copy誰的我忘了,總結(jié)的很好,借用一下哈
從Xcode的線程函數(shù)調(diào)用棧(注意看看)可以看到一些方法調(diào)用順序。
(主線程開啟)start->(加載framework,動(dòng)態(tài)靜態(tài)鏈接庫,啟動(dòng)圖片,Info.plist,pch等)->main函數(shù)->UIApplicationMain函數(shù):{
- 初始化UIApplication單例對象
- 初始化AppDelegate對象,并設(shè)為UIApplication對象的代理
- 檢查Info.plist設(shè)置的xib文件是否有效,如果有則解凍Nib文件并設(shè)置outlets,創(chuàng)建顯示key window、rootViewController、與rootViewController關(guān)聯(lián)的根view(沒有關(guān)聯(lián)則看rootViewController同名的xib),否則launch之后由程序員手動(dòng)加載。
- 建立一個(gè)主事件循環(huán),其中包含UIApplication的Runloop來開始處理事件。
}
UIApplication:
- 通過window管理視圖;
- 發(fā)送Runloop封裝好的control消息給target;
- 處理URL,應(yīng)用圖標(biāo)警告,聯(lián)網(wǎng)狀態(tài),狀態(tài)欄,遠(yuǎn)程事件等。
AppDelegate:
管理UIApplication生命周期和應(yīng)用的五種狀態(tài)(notRunning/inactive/active/background/suspend)。
Key Window:
- 顯示view;
- 管理rootViewcontroller生命周期;
- 發(fā)送UIApplication傳來的事件消息給view。
rootViewController: - 管理view(view生命周期;view的數(shù)據(jù)源/代理;view與superView之間事件響應(yīng)nextResponder的“備胎”);
- 界面跳轉(zhuǎn)與傳值;
- 狀態(tài)欄,屏幕旋轉(zhuǎn)。
view:
- 通過作為CALayer的代理,管理layer的渲染(順序大概是先更新約束,再layout再display)和動(dòng)畫(默認(rèn)layer的屬性可動(dòng)畫,view默認(rèn)禁止,在UIView的block分類方法里才打開動(dòng)畫)。layer是RGBA紋理,通過和mask位圖(含alpha屬性)關(guān)聯(lián)將合成后的layer紋理填充在像素點(diǎn)內(nèi),GPU每1/60秒將計(jì)算出的紋理display在像素點(diǎn)中。
- 布局子控件(屏幕旋轉(zhuǎn)或者子視圖布局變動(dòng)時(shí),view會(huì)重新布局)。
- 事件響應(yīng):event和guesture。
runloop:
- (要讓馬兒跑)通過do-while死循環(huán)讓程序持續(xù)運(yùn)行:接收用戶輸入,調(diào)度處理事件時(shí)間。
- (要讓馬兒少吃草)通過mach_msg()讓runloop沒事時(shí)進(jìn)入trap狀態(tài),節(jié)省CPU資源。
RunLoop面試題
- 什么是RunLoop?
1>從字面意思看:運(yùn)行循環(huán)/跑圈
2>其實(shí)它內(nèi)部就是do-while循環(huán),在這個(gè)循環(huán)內(nèi)部不斷地處理各種任務(wù)(比如:source/Timer/Observer)
3>一個(gè)線程對應(yīng)一個(gè)runloop,主線程的runloop默認(rèn)已經(jīng)啟動(dòng),子線程的runloop需要自己手動(dòng)啟動(dòng)(調(diào)用run方法)
4>RunLoop只能選擇一個(gè)Mode啟動(dòng),如果當(dāng)Mode中沒有任何source(source0/source1),Timer,那么就直接退出RunLoop - 自動(dòng)釋放池什么時(shí)候釋放?
通過Observer監(jiān)聽RunLoop的狀態(tài),一旦監(jiān)聽到RunLoop即將進(jìn)入休眠等待狀態(tài),就釋放自動(dòng)釋放池(KCFRunLoopBeforeWaiting) - 在開發(fā)中如何使用RunLoop?什么應(yīng)用場景?
1>開啟一個(gè)常駐線程(讓一個(gè)子線程不進(jìn)入消亡狀態(tài),等待其他的線程發(fā)來消息,處理其他事件)
在子線程中開啟一個(gè)定時(shí)器
在子線程中進(jìn)行一些長期監(jiān)控
2>可以控制定時(shí)器在特定模式下運(yùn)行
3>可以讓某些事件(行為/任務(wù))在特定模式下執(zhí)行
4>可以添加Observer監(jiān)聽runloop的狀態(tài),比如監(jiān)聽點(diǎn)擊事件的處理(在所有點(diǎn)擊事件之前做一些事情)