Run Loop

運(yùn)行循環(huán),在程序運(yùn)行中循環(huán)做事情,如果沒有runloop程序一運(yùn)行就會(huì)馬上退出

基本作用

保持程序的持續(xù)運(yùn)行
處理App中的各種事件(比如觸摸事件、定時(shí)器事件等)
節(jié)省CPU資源,提高程序性能:該做事時(shí)做事,該休息時(shí)休息

iOS中有2套API來訪問和使用RunLoop

Foundation:NSRunLoop
Core Foundation:CFRunLoopRef
NSRunLoop和CFRunLoopRef都代表著RunLoop對(duì)象
NSRunLoop是基于CFRunLoopRef的一層OC包裝
CFRunLoopRef是開源的 https://opensource.apple.com/tarballs/CF/

RunLoop與線程

每條線程都有唯一的一個(gè)與之對(duì)應(yīng)的RunLoop對(duì)象
RunLoop保存在一個(gè)全局的Dictionary里,線程作為key,RunLoop作為value
線程剛創(chuàng)建時(shí)并沒有RunLoop對(duì)象,RunLoop會(huì)在第一次獲取它時(shí)創(chuàng)建
RunLoop會(huì)在線程結(jié)束時(shí)銷毀
主線程的RunLoop已經(jīng)自動(dòng)獲?。▌?chuàng)建)
子線程默認(rèn)沒有開啟RunLoop

   CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));//線程為key,__CFRunLoops(字典)中取
    __CFUnlock(&loopsLock);
    if (!loop) {
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);//創(chuàng)建runloop
        __CFLock(&loopsLock);
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));//線程為key,runloop為value,存入字典
    if (!loop) {
        CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
        loop = newLoop;
    }

Core Foundation中關(guān)于RunLoop的5個(gè)類

  • CFRunLoopRef - 獲得當(dāng)前RunLoop和主RunLoop
  • CFRunLoopModeRef - RunLoop 運(yùn)行模式,只能選擇一種,在不同模式中做不同的操作
  • CFRunLoopSourceRef - 事件源,輸入源
  • CFRunLoopTimerRef - 定時(shí)器時(shí)間
  • CFRunLoopObserverRef - 觀察者
CFRunLoopRef

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

CFRunLoopModeRef

一個(gè)RunLoop包含若干個(gè)Mode,每個(gè)Mode又包含若干個(gè)Source0/Source1/Timer/Observer
RunLoop啟動(dòng)時(shí)只能選擇其中一個(gè)Mode,作為currentMode
如果需要切換Mode,只能退出當(dāng)前Loop,再重新選擇一個(gè)Mode進(jìn)入
不同組的Source0/Source1/Timer/Observer能分隔開來,互不影響
如果Mode里沒有任何Source0/Source1/Timer/Observer,RunLoop會(huì)立馬退出

常見的2種Mode

kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默認(rèn)Mode,通常主線程是在這個(gè)Mode下運(yùn)行
UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響

有什么事件就處理什么事件,沒有就休眠

  • Source0 觸摸事件處理 performSelector:onThread:
  • Source1 基于Port的線程間通信 系統(tǒng)事件捕捉
  • Timers NSTimer performSelector:withObject:afterDelay:
  • Observers 用于監(jiān)聽RunLoop的狀態(tài) UI刷新(BeforeWaiting) Autorelease pool(BeforeWaiting)
CFRunLoopObserverRef 觀察者,能夠監(jiān)聽RunLoop的狀態(tài)改變

我們直接來看代碼,給RunLoop添加監(jiān)聽者,監(jiān)聽其運(yùn)行狀態(tài)

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
     //創(chuàng)建監(jiān)聽者
     /*
     第一個(gè)參數(shù) CFAllocatorRef allocator:分配存儲(chǔ)空間 CFAllocatorGetDefault()默認(rèn)分配
     第二個(gè)參數(shù) CFOptionFlags activities:要監(jiān)聽的狀態(tài) kCFRunLoopAllActivities 監(jiān)聽所有狀態(tài)
     第三個(gè)參數(shù) Boolean repeats:YES:持續(xù)監(jiān)聽 NO:不持續(xù)
     第四個(gè)參數(shù) CFIndex order:優(yōu)先級(jí),一般填0即可
     第五個(gè)參數(shù) :回調(diào) 兩個(gè)參數(shù)observer:監(jiān)聽者 activity:監(jiān)聽的事件
     */
     /*
     所有事件
     typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
     kCFRunLoopEntry = (1UL << 0),   //   即將進(jìn)入RunLoop
     kCFRunLoopBeforeTimers = (1UL << 1), // 即將處理Timer
     kCFRunLoopBeforeSources = (1UL << 2), // 即將處理Source
     kCFRunLoopBeforeWaiting = (1UL << 5), //即將進(jìn)入休眠
     kCFRunLoopAfterWaiting = (1UL << 6),// 剛從休眠中喚醒
     kCFRunLoopExit = (1UL << 7),// 即將退出RunLoop
     kCFRunLoopAllActivities = 0x0FFFFFFFU
     };
     */
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"RunLoop進(jìn)入");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"RunLoop要處理Timers了");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"RunLoop要處理Sources了");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"RunLoop要休息了");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"RunLoop醒來了");
                break;
            case kCFRunLoopExit:
                NSLog(@"RunLoop退出了");
                break;
                
            default:
                break;
        }
    });
    
    // 給RunLoop添加監(jiān)聽者
    /*
     第一個(gè)參數(shù) CFRunLoopRef rl:要監(jiān)聽哪個(gè)RunLoop,這里監(jiān)聽的是主線程的RunLoop
     第二個(gè)參數(shù) CFRunLoopObserverRef observer 監(jiān)聽者
     第三個(gè)參數(shù) CFStringRef mode 要監(jiān)聽RunLoop在哪種運(yùn)行模式下的狀態(tài)
     */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
     /*
     CF的內(nèi)存管理(Core Foundation)
     凡是帶有Create、Copy、Retain等字眼的函數(shù),創(chuàng)建出來的對(duì)象,都需要在最后做一次release
     GCD本來在iOS6.0之前也是需要我們釋放的,6.0之后GCD已經(jīng)納入到了ARC中,所以我們不需要管了
     */
    CFRelease(observer);
}
RunLoop的運(yùn)行邏輯

01、通知Observers:進(jìn)入Loop
02、通知Observers:即將處理Timers
03、通知Observers:即將處理Sources
04、處理Blocks
05、處理Source0(可能會(huì)再次處理Blocks)
06、如果存在Source1,就跳轉(zhuǎn)到第8步
07、通知Observers:開始休眠(等待消息喚醒)
08、通知Observers:結(jié)束休眠(被某個(gè)消息喚醒)
01> 處理Timer
02> 處理GCD Async To Main Queue
03> 處理Source1
09、處理Blocks
10、根據(jù)前面的執(zhí)行結(jié)果,決定如何操作
01> 回到第02步
02> 退出Loop
11、通知Observers:退出Loop

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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