iOS面試題之runloop

參考:

https://blog.ibireme.com/2015/05/18/runloop/

https://www.jianshu.com/p/fcb271f69038

https://blog.csdn.net/u014795020/article/details/72084735

 

目錄

1、runloop與線程的關係

2、runloop的五種模式

3、runloop內部邏輯

4、 NSAutoreleasePool(自動釋放池)釋放時機

5、基於runloop的線程間通信

6、創建常駐線程 


1、runloop與線程的關係

1、二者相輔相成、一一對應,可以說RunLoop是爲了線程而生,沒有線程,它也沒有存在的必要。其關係是保存在一個全局的 Dictionary 裏。

2、主線程創建時系統自動爲其創建了一個runloop,子線程創建時並沒有 RunLoop,如果你不主動獲取(通過[NSRunLoop currentRunLoop]獲取),那它一直都不會有。

3、RunLoop 的創建是發生在第一次獲取時,RunLoop 的銷燬是發生在線程結束時。你只能在一個線程的內部獲取其 RunLoop(主線程除外)。

 

2、runloop的五種模式

kCFRunLoopDefaultMode(默認模式) 

UITrackingRunLoopMode(UI模式) 

kCFRunLoopCommonModes (NSRunLoopCommonModes佔位模式)。其實佔位模式不是一個真正的模式,它相當於上面兩種模式之和。

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

UIInitializationRunLoopMode: 在剛啓動 App 時第進入的第一個 Mode,啓動完成後就不再使用。
 

3、runloop內部邏輯

RunLoop_1

名詞解釋:

source0:非系統事件;只包含一個回調(函數指針),不能主動觸發事件;使用時,需要先調用CFRunLoopSourceSignal(source);將這個Source標記爲待處理;然後手動調用CFRunLoopWakeUp(runloop)來喚醒Runloop,讓其處理這個事件;

source1:系統事件:包含一個mach_port和一個回調(函數指針),被用於通過內核和其他線程相互發送消息;這種source能主動喚醒Runloop的線程;

timer:使用NSTimer API註冊執行的任務,就屬於這一類;

observer:可以監聽runloop的狀態變化,並作出反應;

 

4、 NSAutoreleasePool(自動釋放池)釋放時機

在runloop準備進入休眠時釋放,也在退出時釋放。

ibireme說:App啓動後,蘋果在主線程 RunLoop 裏註冊了兩個 Observer,其回調都是 _wrapRunLoopWithAutoreleasePoolHandler()。

第一個 Observer 監視的事件是 Entry(即將進入Loop),其回調內會調用 _objc_autoreleasePoolPush() 創建自動釋放池。其 order 是-2147483647,優先級最高,保證創建釋放池發生在其他所有回調之前。

第二個 Observer 監視了兩個事件: BeforeWaiting(準備進入休眠) 時調用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池並創建新池;Exit(即將退出Loop) 時調用 _objc_autoreleasePoolPop() 來釋放自動釋放池。這個 Observer 的 order 是 2147483647,優先級最低,保證其釋放池子發生在其他所有回調之後。

在主線程執行的代碼,通常是寫在諸如事件回調、Timer回調內的。這些回調會被 RunLoop 創建好的 AutoreleasePool 環繞着,所以不會出現內存泄漏,開發者也不必顯示創建 Pool 了。

 

5、基於runloop的線程間通信

基於 端口,在兩個線程中註冊兩個端口,利用sendBeforeDate方法發送內容,用kvc的捕獲屬性components的值。

 

6、創建常駐線程 

爲了減少頻繁創建和銷燬線程的內存消耗,有時需要創建一條常駐線程來執行頻繁的任務。例如AFN:

+ (void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool {
        [[NSThread currentThread] setName:@"AFNetworking"];
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}
 
+ (NSThread *)networkRequestThread {
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
        [_networkRequestThread start];
    });
    return _networkRequestThread;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章