參考:
https://blog.ibireme.com/2015/05/18/runloop/
https://www.jianshu.com/p/fcb271f69038
https://blog.csdn.net/u014795020/article/details/72084735
目錄
4、 NSAutoreleasePool(自動釋放池)釋放時機
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內部邏輯
名詞解釋:
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;
}