RunLoop的基本認(rèn)識(shí)

RunLoop的介紹

  • 什么是RunLoop?
  • Xcode執(zhí)行程序的時(shí)候就先從Main函數(shù)那里開始執(zhí)行的,Main函數(shù)里面實(shí)現(xiàn)了UIApplicationMain,而UIApplicationMain里面啟動(dòng)了一個(gè)RunLoop對(duì)象,所以程序才能一直運(yùn)行,如果沒有RunLoop程序一開始就掛了
  • RunLoop內(nèi)部其實(shí)是一個(gè)do while 循環(huán) (死循環(huán)) 有事件需要處理的時(shí)候通知RunLoop 沒有事件的時(shí)候就讓它進(jìn)入休眠狀態(tài)
示例.png

RunLoop的基本作用

  • 保持程序的持續(xù)運(yùn)行
  • 處理App中的各種事件(如:觸摸事件,定時(shí)器事件)
  • 節(jié)省CPU資源,提高程序性能,(如:程序沒有任務(wù)就進(jìn)入睡眠狀態(tài),有任務(wù)的時(shí)候就喚醒)

iOS程序中又兩套API來訪問和使用RunLoop

1.C語言:Core Foundation -> CFRunLoopRef
2.OC語言的:Foundation -> NSRunLoop
-NSRunLoop 其實(shí)是 CFRunLoopRef 的OC包裝

  • RunLoop與線程
    1.關(guān)系:一個(gè)RunLoop對(duì)應(yīng)著一條唯一的線程
    2,創(chuàng)建:主線程RunLoop已經(jīng)創(chuàng)建好了,子線程的RunLoop需要手動(dòng)創(chuàng)建
    3,生命周期:RunLoop在第一次獲取時(shí)創(chuàng)建,在線程結(jié)束時(shí)銷毀
#如果想讓線程不死,可以手動(dòng)創(chuàng)建一個(gè)RunLoop
void msg(int number)
{
    NSLog(@"runloop被喚醒");
    NSLog(@"執(zhí)行任務(wù)---%d",number);
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        do {
            
            NSLog(@"是否有任務(wù)需要處理,沒有就進(jìn)入休眠狀態(tài)");
            NSLog(@"runloop進(jìn)入到休眠狀態(tài)");
            int number = 0;
            scanf("%d",&number);
            msg(number);
            
        } while (1);
    }
    return 0;
}

[NSRunLoop currentRunLoop]; //當(dāng)前的RunLoop
[NSRunLoop mainRunLoop]; //主線程的RunLoop
在子線程需要手動(dòng)創(chuàng)建RunLoop 創(chuàng)建當(dāng)前的RunLoop即可
RunLoop是懶加載的
RunLoop是用字典來存儲(chǔ)的

RunLoop的相關(guān)類

  # 五個(gè)相關(guān)的類
    a.CFRunloopRef
    b.CFRunloopModeRef【Runloop的運(yùn)行模式】
    c.CFRunloopSourceRef【Runloop要處理的事件源】
    d.CFRunloopTimerRef【Timer事件】
    e.CFRunloopObserverRef【Runloop的觀察者(監(jiān)聽者)】
關(guān)系圖.png

Mode是運(yùn)行模式
一個(gè)RunLoop包含了若干個(gè)模式;
一個(gè)運(yùn)行模型至少要有一個(gè)source 或者 Timer
每次RunLoop啟動(dòng)時(shí),只能指定其中一個(gè)Mode,這個(gè)Mode被稱作CurrentMode
如果需要切換Mode ,只能退出線程,重新指定一個(gè)Mode進(jìn)入

  • 這樣做主要是為了分隔開不同組的 source/Timer/Observer,讓它們互不影響

  • 系統(tǒng)默認(rèn)注冊(cè)了五種Mode

1.kCFRunLoopDefaultMode:App的默認(rèn)Mode,通常主線程是在這個(gè)Mode下運(yùn)行
2.UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響
3.UIInitializationRunLoopMode: 在剛啟動(dòng)App時(shí)第進(jìn)入的第一個(gè) Mode,啟動(dòng)完成后就不再使用
4.GSEventReceiveRunLoopMode:接受系統(tǒng)事件的內(nèi)部Mode,通常用不到
5.kCFRunLoopCommonModes:這是一個(gè)占位用的Mode,不是一種真正的Mode

CFRunloopSourceRef (source事件源)

source0 : 非基于端口的
source1: 基于端口的
可以通過打斷點(diǎn)的方式查看一個(gè)方法的函數(shù)調(diào)用棧

CFRunloopTimerRef (Timer事件)

  • NSTimer收RunLoop的Mode的影響
    • 對(duì)應(yīng)Mode處理對(duì)應(yīng)的事件,增加用戶的體驗(yàn)
/*
    說明:
    (1)runloop一啟動(dòng)就會(huì)選中一種模式,當(dāng)選中了一種模式之后其它的模式就都不鳥。一個(gè)mode里面可以添加多個(gè)NSTimer,也就是說以后當(dāng)創(chuàng)建NSTimer的時(shí)候,可以指定它是在什么模式下運(yùn)行的。
    (2)它是基于時(shí)間的觸發(fā)器,說直白點(diǎn)那就是時(shí)間到了我就觸發(fā)一個(gè)事件,觸發(fā)一個(gè)操作。基本上說的就是NSTimer
*/
- (void)timer1
{
    //NSTimer 調(diào)用了scheduledTimer方法,那么會(huì)自動(dòng)添加到當(dāng)前的runloop里面去,而且runloop的運(yùn)行模式kCFRunLoopDefaultMode

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    //更改模式
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

}

- (void)timer2
{
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    //定時(shí)器添加到UITrackingRunLoopMode模式,一旦runloop切換模式,那么定時(shí)器就不工作
    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

    //定時(shí)器添加到NSDefaultRunLoopMode模式,一旦runloop切換模式,那么定時(shí)器就不工作
    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

    //占位模式:common modes標(biāo)記
    //被標(biāo)記為common modes的模式 kCFRunLoopDefaultMode  UITrackingRunLoopMode
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

    //    NSLog(@"%@",[NSRunLoop currentRunLoop]);
}

- (void)run
{
    NSLog(@"---run---%@",[NSRunLoop currentRunLoop].currentMode);
}

- (IBAction)btnClick {

    NSLog(@"---btnClick---");
}

GCD定時(shí)器不受RunLoop的Mode的影響

GCD定時(shí)器的特點(diǎn):
1,GCD的定時(shí)器不會(huì)受到RunLoop運(yùn)行模式的影響
2,可以控制熱舞在主線程還是子線程執(zhí)行
3,GCD定時(shí)器比NSTimer更加準(zhǔn)確是因?yàn)閱挝徊煌?GCD單位是納秒

//1.創(chuàng)建定時(shí)器對(duì)象
    /*
     第一個(gè)參數(shù):DISPATCH_SOURCE_TYPE_TIMER 創(chuàng)建的是一個(gè)定時(shí)器
     第四個(gè)參數(shù):隊(duì)列,決定在哪個(gè)線程中執(zhí)行任務(wù)
     */
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
    
    dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 3.0 *NSEC_PER_SEC);
    
    //2.設(shè)置定時(shí)器(間隔時(shí)間|開始時(shí)間)
    /*
     第一個(gè)參數(shù):定時(shí)器對(duì)象
     第二個(gè)參數(shù):從什么時(shí)候開始計(jì)時(shí) DISPATCH_TIME_NOW == 從現(xiàn)在開始
     第三個(gè)參數(shù):間隔時(shí)間 2.0 以納秒為單位
     第四個(gè)參數(shù):精準(zhǔn)度(表示允許的誤差)== 0
     */
    dispatch_source_set_timer(timer, t, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    
    //3.定義定時(shí)器的工作
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"----GCD----%@",[NSThread currentThread]);
    });
    
    //4.啟動(dòng)執(zhí)行
    dispatch_resume(timer);

        //注意:dispatch_source_t本質(zhì)上是OC類,在這里是個(gè)局部變量,需要強(qiáng)引用
    self.timer = timer;
.

CFRunloopObserverRef(observer觀察者,監(jiān)聽者)

作用: 監(jiān)聽運(yùn)行循環(huán)的狀態(tài)
如何監(jiān)聽:
1,創(chuàng)建監(jiān)聽對(duì)象
2,給RunLoop添加監(jiān)聽者
3,注意對(duì)象的釋放

 //1.創(chuàng)建監(jiān)聽者
    /*
     第一個(gè)參數(shù):分配存儲(chǔ)空間
     第二個(gè)參數(shù):要監(jiān)聽runloop的什么狀態(tài)
     第三個(gè)參數(shù):持續(xù)監(jiān)聽 == YES
     第四個(gè)參數(shù):傳0
     */
    CFRunLoopObserverRef observer =
CFRunLoopObserverCreateWithHandler
(CFAllocatorGetDefault(),kCFRunLoopAllActivities, YES, 0, 
^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        
        /*
         kCFRunLoopEntry = (1UL << 0),         runloop啟動(dòng)
         kCFRunLoopBeforeTimers = (1UL << 1),  runloop即將處理定時(shí)器事件
         kCFRunLoopBeforeSources = (1UL << 2), runloop即將處理source事件
         kCFRunLoopBeforeWaiting = (1UL << 5), runloop即將休眠
         kCFRunLoopAfterWaiting = (1UL << 6),  runloop被喚醒
         kCFRunLoopExit = (1UL << 7),          退出
         kCFRunLoopAllActivities = 0x0FFFFFFFU
         */
        
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"runloop啟動(dòng)");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"runloop即將處理定時(shí)器事件");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"runloop即將處理source事件");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"runloop即將休眠");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"runloop被喚醒");
                break;
            case kCFRunLoopExit:
                NSLog(@"runloop退出");
                break;
                
            default:
                break;
        }
    });
    
    
    //2.設(shè)置監(jiān)聽的runloop和運(yùn)行模式
    /*
     第一個(gè)參數(shù):runloop對(duì)象
     第二個(gè)參數(shù):監(jiān)聽者
     第三個(gè)參數(shù):runloop的運(yùn)行模式
     第四個(gè)參數(shù):
     */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    
    //3.需要釋放
    CFRelease(observer);

RunLoop處理邏輯-官方.png

上圖顯示了線程的輸入源

1.基于端口的輸入源(Port Sources)
2.自定義輸入源(Custom Sources)
3.Cocoa執(zhí)行Selector的源(performSelectorxxxx
方法)
4.定時(shí)源(Timer Sources )

線程針對(duì)上面不同的輸入源,有不同的處理機(jī)制

1.handlePort——處理基于端口的輸入源
2.customSrc——處理用戶自定義輸入源
3.mySelector——處理Selector的源
4.timerFired——處理定時(shí)源

RunLoop的應(yīng)用場(chǎng)

應(yīng)用場(chǎng)景.png
  • 常駐線程

  • 比如網(wǎng)絡(luò)請(qǐng)求,語音消息,可以創(chuàng)建一個(gè)子線程專門處理語音消息的代碼

  • 一直在后臺(tái)做耗時(shí)操作,不想讓子線程銷毀,那么久用一個(gè)do while 循環(huán),在do while循環(huán)管里面處理事件

  • 自動(dòng)釋放池什么時(shí)候釋放?

  • 啟動(dòng)RunLoop的時(shí)候會(huì)創(chuàng)建自動(dòng)釋放池,RunLoop退出的時(shí)候就會(huì)讓自動(dòng)釋放池銷毀

  • 當(dāng)RunLoop進(jìn)入休眠的時(shí)候,就會(huì)讓自動(dòng)釋放池之前的銷毀,然后重新創(chuàng)建一個(gè)新的自動(dòng)釋放池(因?yàn)樵谛菝郀顟B(tài),沒有事情做,所以自動(dòng)釋放池也沒用)

#Author: 會(huì)跳舞的獅子
最后編輯于
?著作權(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ù)。

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

  • 一、什么是runloop 字面意思是“消息循環(huán)、運(yùn)行循環(huán)”。它不是線程,但它和線程息息相關(guān)。一般來講,一個(gè)線程一次...
    WeiHing閱讀 8,305評(píng)論 11 111
  • Run loop 剖析:Runloop 接收的輸入事件來自兩種不同的源:輸入源(intput source)和定時(shí)...
    Mitchell閱讀 12,640評(píng)論 17 111
  • 這是AF2.x經(jīng)典的代碼: 首先我們要明確一個(gè)概念,線程一般都是一次執(zhí)行完任務(wù),就銷毀了。 而添加了runloop...
    有夢(mèng)想的老伯伯閱讀 2,083評(píng)論 5 13
  • 首先看一段AF2.x經(jīng)典代碼: 首先我們要明確一個(gè)概念,線程一般都是一次執(zhí)行完任務(wù),就銷毀了。 而添加了runlo...
    涂耀輝閱讀 22,878評(píng)論 42 293
  • 當(dāng)你明白的時(shí)候 你是生命的源頭 你會(huì)聽見下雪的聲音 會(huì)聽見草葉的低語和夜的嘆息 你的覺知 會(huì)觸摸到無形 當(dāng)你明白的...
    清枚閱讀 252評(píng)論 0 0

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