RunLoop

什么是RunLoop

  • 從字面意思看

  • 運(yùn)行循環(huán)

  • 跑圈

  • 基本作用

  • 保持程序的持續(xù)運(yùn)行

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

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

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ù)一直沒(méi)有返回,保持了程序的持續(xù)運(yùn)行
  • 這個(gè)默認(rèn)啟動(dòng)的RunLoop是跟主線程相關(guān)聯(lián)的

RunLoop對(duì)象

  • iOS中有2套API來(lái)訪問(wèn)和使用RunLoop

  • Foundation

    • NSRunLoop
  • Core Foundation

    • CFRunLoopRef
  • NSRunLoopCFRunLoopRef都代表著RunLoop對(duì)象

  • NSRunLoop是基于CFRunLoopRef的一層OC包裝,所以要了解RunLoop內(nèi)部結(jié)構(gòu),需要多研究CFRunLoopRef層面的API(Core Foundation層面)

RunLoop與線程

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

  • 主線程的RunLoop已經(jīng)自動(dòng)創(chuàng)建好了,子線程的RunLoop需要主動(dòng)創(chuàng)建

  • RunLoop在第一次獲取時(shí)創(chuàng)建,在線程結(jié)束時(shí)銷毀

獲得RunLoop對(duì)象

  • Foundation
[NSRunLoop currentRunLoop]; // 獲得當(dāng)前線程的RunLoop對(duì)象
[NSRunLoop mainRunLoop]; // 獲得主線程的RunLoop對(duì)象
  • Core Foundation
CFRunLoopGetCurrent(); // 獲得當(dāng)前線程的RunLoop對(duì)象
CFRunLoopGetMain(); // 獲得主線程的RunLoop對(duì)象

RunLoop相關(guān)類

  • Core Foundation中關(guān)于RunLoop的5個(gè)類
  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef
RunLoop.png

CFRunLoopModeRef

  • CFRunLoopModeRef代表RunLoop的運(yùn)行模式
  • 一個(gè)RunLoop包含若干個(gè)Mode,每個(gè)Mode又包含若干個(gè)Source/Timer/Observer
  • 每次RunLoop啟動(dòng)時(shí),只能指定其中一個(gè)Mode,這個(gè)Mode被稱作 CurrentMode
  • 如果需要切換Mode,只能退出Loop,再重新指定一個(gè)Mode進(jìn)入
  • 這樣做主要是為了分隔開(kāi)不同組的Source/Timer/Observer,讓其互不影響
  • 系統(tǒng)默認(rèn)注冊(cè)了5個(gè)Mode:
  • kCFRunLoopDefaultMode:App的默認(rèn)Mode,通常主線程是在這個(gè)Mode下運(yùn)行
  • UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響
  • UIInitializationRunLoopMode: 在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode,啟動(dòng)完成后就不再使用
  • GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到
  • kCFRunLoopCommonModes: 這是一個(gè)占位用的Mode,不是一種真正的Mode

CFRunLoopSourceRef

  • CFRunLoopSourceRef是事件源(輸入源)
  • 以前的分法
  • Port-Based Sources
  • Custom Input Sources
  • Cocoa Perform Selector Sources
  • 現(xiàn)在的分法
  • Source0:非基于Port
  • Source1:基于Port

CFRunLoopTimerRef

  • CFRunLoopTimerRef是基于時(shí)間的觸發(fā)器
  • 基本上說(shuō)的就是NSTimer

CFRunLoopObserverRef

  • CFRunLoopObserverRef是觀察者,能夠監(jiān)聽(tīng)RunLoop的狀態(tài)改變
  • 可以監(jiān)聽(tīng)的時(shí)間點(diǎn)有以下幾個(gè)
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),//即將進(jìn)入Loop
    kCFRunLoopBeforeTimers = (1UL << 1),//即將處理Timer
    kCFRunLoopBeforeSources = (1UL << 2),//即將處理Source
    kCFRunLoopBeforeWaiting = (1UL << 5),//即將進(jìn)入休眠
    kCFRunLoopAfterWaiting = (1UL << 6),//剛從休眠中喚醒
    kCFRunLoopExit = (1UL << 7),//即將推出Loop
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

RunLoop處理邏輯-官方版

  • RunLoop官方處理邏輯圖.png
  • RunLoop官方處理邏輯.png

RunLoop處理邏輯-網(wǎng)友整理版

  • RunLoop處理邏輯-網(wǎng)友整理版.png

RunLoop應(yīng)用

  • NSTimer定時(shí)器
  • 通常你會(huì)發(fā)現(xiàn)NSTimer在用戶執(zhí)行一些操作時(shí)會(huì)暫停觸發(fā),這就是跟runloop有關(guān)系的
-(void)timer
{
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    // 定時(shí)器只運(yùn)行在NSDefaultRunLoopMode下,一旦RunLoop進(jìn)入其他模式,這個(gè)定時(shí)器就不會(huì)工作
    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    
    // 定時(shí)器只運(yùn)行在UITrackingRunLoopMode下,一旦RunLoop進(jìn)入其他模式,這個(gè)定時(shí)器就不會(huì)工作
    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
    
    // 定時(shí)器會(huì)跑在標(biāo)記為common modes的模式下
    // 標(biāo)記為common modes的模式:UITrackingRunLoopMode和NSDefaultRunLoopMode
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
-(void)timer2
{
    // 調(diào)用了scheduledTimer返回的定時(shí)器,已經(jīng)自動(dòng)被添加到當(dāng)前runLoop中,而且是NSDefaultRunLoopMode
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    
    // 修改模式
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
  • 在子線程開(kāi)啟一個(gè)定時(shí)器
-(void)viewDidLoad {
    [super viewDidLoad];
    
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(execute) object:nil];
    [self.thread start];
}
-(void)execute
{
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
    
    [[NSRunLoop currentRunLoop] run];
}
  • observer監(jiān)聽(tīng)消息循環(huán)機(jī)制(觀察者)
/*
    CF的內(nèi)存管理(Core Foundation)
    1.凡是帶有Create、Copy、Retain等字眼的函數(shù),創(chuàng)建出來(lái)的對(duì)象,都需要在最后做一次release
    * 比如CFRunLoopObserverCreate
    2.release函數(shù):CFRelease(對(duì)象);
 */
-(void)observer
{
    // 創(chuàng)建observer
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        NSLog(@"----監(jiān)聽(tīng)到RunLoop狀態(tài)發(fā)生改變---%zd", activity);
    });

    // 添加觀察者:監(jiān)聽(tīng)RunLoop的狀態(tài)
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    
    // 釋放Observer
    CFRelease(observer);
}
  • PerformSelector使用和 ImageView顯示
  • 有時(shí)候?yàn)榱藘?yōu)化性能,在用戶滑動(dòng)交互時(shí)暫停圖片的加載,使得界面滑動(dòng)更加流暢
// 只在NSDefaultRunLoopMode模式下顯示圖片
    [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"placeholder"] afterDelay:3.0 inModes:@[NSDefaultRunLoopMode]];
  • 常駐線程
  • 蘋(píng)果幫我們管理線程時(shí),方法結(jié)束(下面的run方法)線程就會(huì)被殺死,即使被@property (nonatomic, strong) NSThread *thread;全局變量引用,run方法結(jié)束時(shí)內(nèi)存中雖然還有這個(gè)線程,但是該線程也已經(jīng)處于了消亡狀態(tài),RunLoop可以幫我們使一個(gè)線程不會(huì)執(zhí)行完run方法,這樣該線程就不會(huì)消亡。
  • 創(chuàng)建一個(gè)線程
//創(chuàng)建一個(gè)線程并開(kāi)啟
-(void)viewDidLoad {
    [super viewDidLoad]; 
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    [self.thread start];
}
  • 線程執(zhí)行的方法,[[NSRunLoop currentRunLoop] run];可以自動(dòng)創(chuàng)建一個(gè)RunLoop,并將其開(kāi)啟,但是請(qǐng)注意,這樣的RunLoop中的source timer observer都為NULL這樣的RunLoop會(huì)自動(dòng)退出,并不能實(shí)現(xiàn)線程不死的效果,所以我們通過(guò)這個(gè)方法 [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];隨便給RunLoop添加一個(gè)source。這樣就開(kāi)啟了一個(gè)RunLoop,下面這個(gè)run方法永遠(yuǎn)不會(huì)執(zhí)行完,所以第二次打印NSLog(@"---------");永遠(yuǎn)不會(huì)執(zhí)行
//線程執(zhí)行的方法
-(void)run
{
//打印當(dāng)前線程
    NSLog(@"----------run----%@", [NSThread currentThread]);
//子線程需要手動(dòng)創(chuàng)建runloop
    [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
    NSLog(@"---------");
//    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
//    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
}
  • 測(cè)試該線程是否死掉,點(diǎn)擊屏幕,觸發(fā)子線程執(zhí)行test方法
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}
-(void)test
{
    NSLog(@"----------test----%@", [NSThread currentThread]);
}
  • 自動(dòng)釋放池
    @autoreleasepool {}將一些對(duì)象放入自動(dòng)釋放池,在runloop睡覺(jué)之前CFRunLoopBeforeWaiting會(huì)清理一次,在runloop喚醒時(shí)kCFRunLoopAfterWaiting會(huì)創(chuàng)建自動(dòng)釋放池
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 基本概念 進(jìn)程 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,而且每個(gè)進(jìn)程之間是獨(dú)立的,它們都運(yùn)行在其專用且受保護(hù)的內(nèi)存...
    小楓123閱讀 1,011評(píng)論 0 1
  • 一、什么是runloop 字面意思是“消息循環(huán)、運(yùn)行循環(huán)”。它不是線程,但它和線程息息相關(guān)。一般來(lái)講,一個(gè)線程一次...
    WeiHing閱讀 8,306評(píng)論 11 111
  • runtime 和 runloop 作為一個(gè)程序員進(jìn)階是必須的,也是非常重要的, 在面試過(guò)程中是經(jīng)常會(huì)被問(wèn)到的, ...
    made_China閱讀 1,273評(píng)論 0 7
  • runtime 和 runloop 作為一個(gè)程序員進(jìn)階是必須的,也是非常重要的, 在面試過(guò)程中是經(jīng)常會(huì)被問(wèn)到的, ...
    SOI閱讀 22,025評(píng)論 3 63
  • 什么是Run Loops RunLoops是與線程相關(guān)聯(lián)的基礎(chǔ)部分,一個(gè)Run Loop就是事件處理循環(huán),他是用來(lái)...
    傻傻小蘿卜閱讀 1,101評(píng)論 0 5

友情鏈接更多精彩內(nèi)容