RunLoop理解與應(yīng)用

什么是RunLoop

簡單的來說就是運(yùn)行循環(huán)、跑圈。

  • 保持著程序的持續(xù)運(yùn)行并接受用戶輸入
  • 處理APP中的各種事件(觸摸,定時器,Selector)
  • 節(jié)省了CPU資源,提高程序性能,即該做事時做事,該休息時休息。

RunLoop對象

iOS中有2套API來訪問RunLoop

  • Foundation
    NSRunloop
  • Core Foundation
    CFRunLoopRef

CFRunLoopRef是用C語言寫的更底層一些。NSRunLoop其實(shí)是對CFRunLoopRef的一個簡單封裝,CFRunLoopRef的性能高一點(diǎn)

獲取RunLoop對象

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

RunLoop與線程間的關(guān)系

  • 每條線程都有唯一的一個與之對應(yīng)的RunLoop對象
  • 主線程的RunLoop已經(jīng)自動創(chuàng)建完成,子線程的需要手動創(chuàng)建
  • RunLoop在第一次獲取時創(chuàng)建,在線程結(jié)束時銷毀

CFRunLoopRef

image.png
  • CFRunLoopRefModeRef
    代表RunLoop的運(yùn)行模式。
kCFRunLoopDefaultMode //app的默認(rèn)Mode,通常主線程是在這個Mode下運(yùn)行
UITrackingRunLoopMode //界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響
UIInitializationRunLoopMode //在剛啟動 App 時第進(jìn)入的第一個 Mode,啟動完成后就不再使用
GSEventReceiveRunLoopMode //接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到
kCFRunLoopCommonModes //這是一個占位用的Mode,不是一種真正的Mode
  • CFRunLoopObserverRef
    觀察者,能夠監(jiān)聽RunLoop的狀態(tài)變化。
kCFRunLoopEntry //即將進(jìn)入loop
kCFRunLoopBeforeTimers //即將處理timer
kCFRunLoopBeforeSources //即將處理source
kCFRunLoopBeforeWaiting //即將進(jìn)入休眠
kCFRunLoopAfterWaiting //即將休眠中喚醒
kCFRunLoopExit  //即將退出loop
kCFRunLoopAllActivities //所有狀態(tài)
  • CFRunLoopSourceRef
    RunLoop要處理的事件源,定義了兩種Source
    Source0:處理APP內(nèi)部時間,APP自己負(fù)責(zé)管理(觸發(fā)),如UIevent...基于非Port的
    Source1:由RunLoop和內(nèi)核管理的系統(tǒng)的一些事件,基于Port的。
  • CFRunLoopTimerRef
    是基于事件的觸發(fā)器,即NSTimer

創(chuàng)建一個RunLoop之后,有默認(rèn)的運(yùn)行模式mode,也可以為RunLoop指定運(yùn)行模式,RunLoop啟動必須得有運(yùn)行模式,而且在運(yùn)行模式中必須還有timer或是source事件其中之一,否則RunLoop就會退出。CFRunLoopModeRef代表RunLoop的運(yùn)行模式
一個 RunLoop 包含若干個Mode,每個Mode又包含若干個SourceTimer、Observer
每次RunLoop啟動時,只能指定其中一個Mode,這個Mode被稱作 CurrentMode
如果需要切換Mode,只能退出RunLoop,再重新指定一個Mode進(jìn)入,這樣做主要是為了分隔開不同組的Source、TimerObserver,讓其互不影響。

image.png
image.png
     /*
        1參:怎么分配存儲空間
        2參:眼監(jiān)聽的狀態(tài) kCFRunLoopAllActivities 所有狀態(tài)
        3參:是否持續(xù)監(jiān)聽
        4參:優(yōu)先級 總是傳0
        5參:當(dāng)狀態(tài)改變時候的回調(diào)
     */
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"即將進(jìn)入runloop");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"即將處理timer事件");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"即將處理source事件");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"即將進(jìn)入睡眠");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"被喚醒");
            case kCFRunLoopExit:
                NSLog(@"runloop退出");
                break;
            default:
                break;
        }
    });
    /*
        1參:要監(jiān)聽那個runloop
        2參:觀察者
        3參:運(yùn)行模式
     */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    CFRelease(observer);//移除

NSRunLoop

  • NSTimer問題
    NSTimer使用scheduledTimerWithTimeInterval此方法創(chuàng)建的定時器默認(rèn)加到了NSRunLoop中,并且設(shè)置運(yùn)行模式為默認(rèn)default,但是如果有滾動事件的時候,定時器就會停止工作。
    解決辦法:更改NSRunLoop的Mode為NSRunLoopCommonModes,滾動拖拽頁面時定時器仍然工作。
//1.創(chuàng)建定時器
NSTimer *timer =  [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];
//添加至RunLoop并更改運(yùn)行模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//開啟runloop
[currentRunloop run];
  • 線程常駐
    創(chuàng)建類GYThread繼承NSThread。GYThread.m中添加dealloc
 - (void)dealloc{
    NSLog(@"線程被銷毀了");
 } 

初始化線程,并創(chuàng)建該線程的RunLoop,s

_thread = [[GYThread alloc]initWithTarget:self selector:@selector(threadtest) object:nil];
 _thread.name = @"geyang";
[_thread start];

 - (void)threadtest{
    NSLog(@"--%@--", [NSThread currentThread]);
    //啟動該?線程的runloop
    [[NSRunLoop currentRunLoop]addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop]run];
 }
//檢測線程是否被銷毀
 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self performSelector:@selector(threadAgain) onThread:_thread withObject:nil waitUntilDone:NO];
}
 - (void)threadAgain{
    NSLog(@"---%@---", [NSThread currentThread]);
}

此時,我們可以點(diǎn)擊屏幕可看到打印結(jié)果

2017-07-13 15:23:24.382 RunLoop與線程[9290:1054512] ---<GYThread: 0x608000269300>{number = 3, name = geyang}---

說明該線程并沒有被銷毀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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