iOS面試-Runloop簡單介紹

一. Runloop基本作用

1)保持程序的持續(xù)運行

  1. 處理APP中各種事件(比如觸摸事件,定時器事件,selector事件)

  2. 節(jié)省cup資源,提高程序性能:該做事時做事,該休息時休息。

main函數(shù)中的runLoop
int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

代碼UIApplicationMain函數(shù)內部啟動了一個runLoop,所以UIApplicationMain函數(shù)一直沒有返回,保持程序的持續(xù)運行。這個默認啟動runloop是跟主線程關聯(lián)的。
點擊UIApplicationMain這個函數(shù)我們發(fā)現(xiàn),它是有返回值的, UIKIT_EXTERN int UIApplicationMain(int argc, char * _Nonnull * _Null_unspecified argv, NSString * _Nullable principalClassName, NSString * _Nullable delegateClassName);,這里我們可以測試一下,UIApplicationMain函數(shù)內部是一直執(zhí)行的,是啟動了一個runloop的,測試代碼如下:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSLog(@"-------star--------");
        int number =  UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        NSLog(@"number:%d",number);
        return number;
    }
}

控制臺輸出如下:

2018-09-22 18:08:16.417670+0800 123Demo[919:39945] -------star--------

這里我們發(fā)現(xiàn)我們的number并沒有被輸出,因為UIApplicationMain函數(shù)一直沒有返回,UIApplicationMain函數(shù)內部一直在執(zhí)行。

二. RunLoop對象

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

  • Foundation NSRunLoop
  • Core Foundation CFRunLoopRef

Runloop相關資料 https://opensource.apple.com/source/CF/CF-1151.16/
鏈接 https://pan.baidu.com/s/14xMqWH47X5tex0KPjxkMAw 密碼:lx8u

三. RunLoop與線程
  • Foundation

[NSRunLoop currentRunLoop]; // 獲得當前線程的NSRunLoop對象
[NSRunLoop mainRunLoop]; // 獲得主線程的NSRunLoop對象

  • Core Foundation

CFRunLoopGetCurrent(); // 獲得當前線程的NSRunLoop對象
CFRunLoopGetMain(); // 獲得主線程的NSRunLoop對象

這里,我們可以看下CFRunLoop.c里面的核心源碼

   // Do any additional setup after loading the view, typically from a nib.
    // should only be called by Foundation
    // t==0 is a synonym for "main thread" that always works
    CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
        if (pthread_equal(t, kNilPthreadT)) {
            t = pthread_main_thread_np();
        }
        __CFLock(&loopsLock);
        if (!__CFRunLoops) {
            __CFUnlock(&loopsLock);
            // 創(chuàng)建字典
            CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
            // 創(chuàng)建主線程對應的runloop
            CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
            // 使用字典保存主線程-主線程對應的runloop
            CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
            if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
                CFRelease(dict);
            }
            CFRelease(mainLoop);
            __CFLock(&loopsLock);
        }
        // 從字典中獲取子線程的runloop
        CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
        __CFUnlock(&loopsLock);
        if (!loop) {
            // 如果子線程的runloop不存在,那么就為該線程創(chuàng)建一個對應的runloop
            CFRunLoopRef newLoop = __CFRunLoopCreate(t);
            __CFLock(&loopsLock);
            loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
            // 把當前子線程和對應的runloop保存到字典中
            if (!loop) {
                CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
                loop = newLoop;
            }
            // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
            __CFUnlock(&loopsLock);
            CFRelease(newLoop);
        }
        if (pthread_equal(t, pthread_self())) {
            _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
            if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
                _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
            }
        }
        return loop;
    }

  • Tips
    每條線程都有唯一的一個與之對應的RunLoop對象.
    主線程RunLoop已經(jīng)自動創(chuàng)建好了,子線程的RunLoop需要主動創(chuàng)建
    RunLoop在第一次獲取是創(chuàng)建,在線程結束時銷毀
三. RunLoop相關類
  • Core Foundation中關于RunLoop的5個類

CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef

runloop.png

在RunLoop中有多個運行模式,但是RunLoop只能選擇一種運行模式(如:一臺空調有制冷和制熱兩種模式,但是啟動后只能選擇一種模式)。

CFRunLoopModeRef代表RunLoop的運行模式:

一個RunLoop包含若干個Mode,每個Mode又包含若干個source/timer/oberrver。model里面至少要有一個timer或者是source
每次RunLoop啟動時,只能選擇其中一個mode,這個mode被稱作currentMode.
如果需要切換mode,只能退出Loop,再重新指定一個Mode進入

CFRunLoopModeRef系統(tǒng)默認注冊了5個Mode:

  1. kCFRunLoopDefaultMode: App的默認 Mode,通常主線程是在這個 Mode 下運行的。

  2. UITrackingRunLoopMode: 界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響。

  3. UIInitializationRunLoopMode: 在剛啟動 App 時第進入的第一個 Mode,啟動完成后就不再使用。

4: GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內部 Mode,通常用不到。

5: kCFRunLoopCommonModes: 這是一個占位的 Mode,沒有實際作用。

kCFRunLoopCommonModes模式有一個比較經(jīng)典的例子,實現(xiàn)一個精準的timer。
例:一個tableview上有一個定時器,這時應該用kCFRunLoopCommonModes模式。

- (void)timer {
    // 1.創(chuàng)建定時器
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    // 2.添加定時器到runloop中
    /**
     第一個參數(shù):定時器
     第二個參數(shù):runloop的運行模式
     */
    [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];

    
}
- (void)run {
    NSLog(@"run----%@ --- %@",[NSThread currentThread],[NSRunLoop currentRunLoop].currentMode);
}
  • Tips
    定時器在子線程中創(chuàng)建,該定時器不會工作,需要run開啟
    主線程RunLoop默認自動創(chuàng)建,子線程的RunLoop需要主動創(chuàng)建
  NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
  ......定時器.....  
  //開啟runloop
  [currentRunLoop run];
    
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 1 Runloop機制原理 深入理解RunLoop http://www.cocoachina.com/ios/2...
    Kevin_Junbaozi閱讀 4,241評論 4 30
  • 概述 RunLoop作為iOS中一個基礎組件和線程有著千絲萬縷的關系,同時也是很多常見技術的幕后功臣。盡管在平時多...
    陽明AI閱讀 1,159評論 0 17
  • Runloop 是和線程緊密相關的一個基礎組件,是很多線程有關功能的幕后功臣。盡管在平常使用中幾乎不太會直接用到,...
    jackyshan閱讀 10,016評論 10 75
  • 這幾天研究了一下iOS的Runloop,看了不少的文章,收獲不少,但是疑問也挺多。所以我就試著去翻譯了并分析總結了...
    擰發(fā)條鳥xds閱讀 1,502評論 0 10
  • Run loop 剖析:Runloop 接收的輸入事件來自兩種不同的源:輸入源(intput source)和定時...
    Mitchell閱讀 12,642評論 17 111

友情鏈接更多精彩內容