RunLoop

什么是RunLoop ?

  • 可以把RunLoop當(dāng)做是一個無限的死循環(huán),正因為有了這種循環(huán),程序才會在執(zhí)行完一遍后也不會掛掉,如果沒有殺死程序進(jìn)程,程序會一直監(jiān)聽app中的各種事件(比如觸摸事件,定時器事件,selector事件)但不同于死循環(huán)的是RunLoop會在事件監(jiān)聽的過程中處于休眠狀態(tài),該做事時做事,該休息時休息,這樣可以節(jié)省cpu資源,提高程序性能
  • 一個線程對應(yīng)一個RunLoop,主線程RunLoop默認(rèn)已經(jīng)開啟,而子線程的RunLoop需要手動開啟(調(diào)用run方法)子線程結(jié)束時RunLoop銷毀

獲取RunLoop對象 Foundation

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

獲取RunLoop對象 Core Foundation

    CFRunLoopGetCurrent();  // 獲得當(dāng)前線程的RunLoop對象
    CFRunLoopGetMain();    //獲得主線程的RunLoop對象

RunLoop結(jié)構(gòu)

Paste_Image.png
  • Core Foundation中關(guān)于RunLoop的五個類
    CFRunLoopRef
    CFRunLoopModeRef
    CFRunLoopSourceRef
    CFRunLoopTimerRef
    CFRunLoopObserverRef
  • CFRunLoopModeRef代表RunLoop的運行模式
  • 一個 RunLoop 包含若干個 Mode,每個Mode又包含若干個Source/Timer/Observer
  • 每次RunLoop啟動時,只能指定其中一個 Mode,這個Mode被稱作 CurrentMode
  • 如果需要切換Mode,只能退出Loop,再重新指定一個Mode進(jìn)入
  • 這樣做主要是為了分隔開不同組的Source/Timer/Observer,讓其互不影響

CFRunLoopModeRef

系統(tǒng)默認(rèn)注冊了5個mode

  • NSDefaultRunLoopMode:App的默認(rèn)Mode,通常主線程是在這個Mode下運行
  • UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響
  • UIInitializationRunLoopMode: 在剛啟動 App 時第進(jìn)入的第一個 Mode,啟動完成后就不再使用
  • GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到
  • NSRunLoopCommonModes: 這是一個占位用的Mode,不是一種真正的Mode,從表面上看這是一個models,也就是一個模式的數(shù)組包含默認(rèn)模式和界面跟鐘模式,實際上他會處理兩種模式下同時產(chǎn)生的各種事件

CFRunLoopTimerRef

CFRunLoopTimerRef是基于時間的觸發(fā)器
CFRunLoopTimerRef基本上說的就是NSTimer,它受RunLoop的Mode影響
GCD的定時器不受RunLoop的Mode影響

CFRunLoopSourceRef (事件源 輸入源)

按照官方文檔,Source的分類
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources

CFRunLoopObserverRef

(CFRunLoopObserverRef是觀察者,能夠監(jiān)聽RunLoop的狀態(tài)改變)

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),                    //即將進(jìn)入RunLoop
    kCFRunLoopBeforeTimers = (1UL << 1),      //即將處理Timer
    kCFRunLoopBeforeSources = (1UL << 2),    //即將處理Sources
    kCFRunLoopBeforeWaiting = (1UL << 5),    //即將進(jìn)入休眠
    kCFRunLoopAfterWaiting = (1UL << 6),       //剛從休眠中喚醒
    kCFRunLoopExit = (1UL << 7),                        //即將退出RunLoop
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

給主線程runloop添加觀察者

// 1.創(chuàng)建一個監(jiān)聽對象
    /*
     第一個參數(shù): 告訴系統(tǒng)如何給Observer對象分配存儲空間
     第二個參數(shù): 需要監(jiān)聽的類型
     第三個參數(shù): 是否需要重復(fù)監(jiān)聽
     第四個參數(shù): 優(yōu)先級
     第五個參數(shù): 監(jiān)聽到對應(yīng)的狀態(tài)之后的回調(diào)
     */
    
    CFRunLoopObserverRef observer =  CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        //        NSLog(@"%lu", 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(@"剛剛從睡眠中醒來");
                break;
            case kCFRunLoopExit:
                NSLog(@"退出RunLoop");
                break;
                
            default:
                break;
        }
    });
    
    // 2.給主線程的RunLoop添加監(jiān)聽
    /*
     第一個參數(shù):需要監(jiān)聽的RunLoop對象
     第二個參數(shù):給指定的RunLoop對象添加的監(jiān)聽對象
     第三個參數(shù):在那種模式下監(jiān)聽
     */
   
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
   
    //3.釋放Observer
    CFRelease(observer);

RunLoop處理邏輯-官方版

Paste_Image.png

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

Paste_Image.png

常駐線程

應(yīng)用場景:開發(fā)中要頻繁的執(zhí)行某一個耗時操作,通常會把這個耗時的操作放在子線程中執(zhí)行,而系統(tǒng)默認(rèn)子線程中任務(wù)執(zhí)行完后這條子線程就會銷毀,
注意 這里千萬不要以為只要使用全局屬性強(qiáng)引用這條線程就可以繼續(xù)使用這條線程了,
測試代碼: 1/2

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
    self.thread.name = @"我是常駐線程";
    [self.thread start];
}

- (void)demo
{
    //  獲取當(dāng)前線程
    NSLog(@"%@--%d",[NSThread currentThread],__LINE__);

    // 獲取當(dāng)前runLoop對象
     NSRunLoop *runLoop =[NSRunLoop currentRunLoop];
    // 以下代碼的目的是為了保證runloop不死   一個NSRunLoop中, 如果沒有source或者timer, 那么NSRunLoop就會退出死循環(huán)
     [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
     //  運行runLoop對象
    [runLoop run];
    
    NSLog(@"如果開啟運行循環(huán)  我不會被打印出來");
}

打印結(jié)果:

Paste_Image.png

測試代碼: 2/2

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //  獲取當(dāng)前線程
    NSLog(@"%@--%d",[NSThread currentThread],__LINE__);
    // 主線程
   [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES];
}

- (void)test
{
    //  獲取當(dāng)前線程
    NSLog(@"%@--%d",[NSThread currentThread],__LINE__);
    NSLog(@"已確認(rèn)可以繼續(xù)使用這條線程做事");
}

打印結(jié)果

**2016-04-07 14:34:37.858 08-RunLoop****應(yīng)用場景****[3069:184521] <NSThread: 0x7fb60ac08a10>{number = 1, name = main}****--****49**
**2016-04-07 14:34:37.859 08-RunLoop****應(yīng)用場景****[3069:184863] <NSThread: 0x7fb60af4af30>{number = 2, name = ****我是常駐線程****}****--****57**
**2016-04-07 14:34:37.859 08-RunLoop****應(yīng)用場景****[3069:184863] ****已確認(rèn)可以繼續(xù)使用這條線程做事**
Paste_Image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 如果沒有RunLoop main函數(shù)中的RunLoop 第14行代碼的UIApplicationMain函數(shù)內(nèi)部就...
    JonesCxy閱讀 667評論 0 4
  • 1 RunLoop簡介 神秘的RunLoop。一個應(yīng)用開始運行以后放在那里,如果不對它進(jìn)行任何操作,這個應(yīng)用就像靜...
    Claire_wu閱讀 1,882評論 3 30
  • RunLoop 文章目錄 RunLoop簡介 1.1 什么是RunLoop? 1.2 RunLoop和線程 1.3...
    May_d8f1閱讀 347評論 0 1
  • 一、什么是runloop 字面意思是“消息循環(huán)、運行循環(huán)”。它不是線程,但它和線程息息相關(guān)。一般來講,一個線程一次...
    WeiHing閱讀 8,309評論 11 111
  • 夕陽西下,別再去留戀今日的溫暖,因為明天太陽依然會升起。 攝于2017年2月26日
    書香會閱讀 245評論 0 1

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