NSRunLoop:
OSX/iOS 系統(tǒng)中,提供了兩個(gè)這樣的對象:NSRunLoop 和 CFRunLoopRef。
CFRunLoopRef 是在 CoreFoundation 框架內(nèi)的,它提供了純 C 函數(shù)的 API,所有這些 API 都是線程安全的。
NSRunLoop 是基于 CFRunLoopRef 的封裝,提供了面向?qū)ο蟮?API,但是這些 API 不是線程安全的。
NSRunLoop 與線程的關(guān)系:
其實(shí)runLoop就是一個(gè)do … while()函數(shù),每個(gè)runLoop對應(yīng)一個(gè)線程他們是一一對應(yīng)的關(guān)系,關(guān)系保存在一個(gè)全局的Dictionary里邊,線程剛創(chuàng)建時(shí)沒有RunLoop,如果不主動獲取,是不會有的,RunLoop的創(chuàng)建發(fā)生在第一次獲取時(shí),RunLoop的銷毀發(fā)生在線程結(jié)束,只能在一個(gè)線程的內(nèi)部獲取它的RunLoop(主線程除外)主線程默認(rèn)有個(gè)RunLoop.
Thread包含一個(gè)CFRunLoop,一個(gè)CFRunLoop包含一種CFRunLoopMode,mode包含CFRunLoopSource,CFRunLoopTimer和CFRunLoopObserver。
Runloop的寄生于線程:一個(gè)線程只能有唯一對應(yīng)的runloop;但這個(gè)根runloop里可以嵌套子runloops;
自動釋放池寄生于Runloop:程序啟動后,主線程注冊了兩個(gè)Observer監(jiān)聽runloop的進(jìn)出與睡覺。一個(gè)最高優(yōu)先級OB監(jiān)測Entry狀態(tài);一個(gè)最低優(yōu)先級OB監(jiān)聽BeforeWaiting狀態(tài)和Exit狀態(tài)。
線程(創(chuàng)建)-->runloop將進(jìn)入-->最高優(yōu)先級OB創(chuàng)建釋放池-->runloop將睡-->最低優(yōu)先級OB銷毀舊池創(chuàng)建新池-->runloop將退出-->最低優(yōu)先級OB銷毀新池-->線程(銷毀)
RunLoop只能運(yùn)行在一種mode下,如果要換mode當(dāng)前的loop也需要停下重啟成新的。利用這個(gè)機(jī)制,ScrollView過程中NSDefaultRunLoopMode的mode會切換UITrackingRunLoopMode來保證ScrollView的流暢滑動不受只能在NSDefaultRunLoopMode時(shí)處理的事件影響滑動。同時(shí)mode還是可定制的。
NSDefaultRunLoopMode:默認(rèn),空閑狀態(tài)
UITrackingRunLoopMode:ScrollView滑動時(shí)
UIInitializationRunLoopMode:啟動時(shí)
NSRunLoopCommonModes:Mode集合 Timer計(jì)時(shí)會被scrollView的滑動影響的問題可以通過將timer添加到NSRunLoopCommonModes來解決
//然后再添加到NSRunLoopCommonModes里
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:@selector(timerTick:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
/// 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
/// 訪問 loopsDic 時(shí)的鎖
static CFSpinLock_t loopsLock;
/// 獲取一個(gè) pthread 對應(yīng)的 RunLoop。
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
OSSpinLockLock(&loopsLock);
if (!loopsDic) {
// 第一次進(jìn)入時(shí),初始化全局Dic,并先為主線程創(chuàng)建一個(gè) RunLoop。
loopsDic = CFDictionaryCreateMutable();
CFRunLoopRef mainLoop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
}
/// 直接從 Dictionary 里獲取。
CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
if (!loop) {
/// 取不到時(shí),創(chuàng)建一個(gè)
loop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, thread, loop);
/// 注冊一個(gè)回調(diào),當(dāng)線程銷毀時(shí),順便也銷毀其對應(yīng)的 RunLoop。
_CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
}
OSSpinLockUnLock(&loopsLock);
return loop;
}
CFRunLoopRef CFRunLoopGetMain() {
return _CFRunLoopGet(pthread_main_thread_np());
}
CFRunLoopRef CFRunLoopGetCurrent() {
return _CFRunLoopGet(pthread_self());
}
NSTimer和performSEL方法實(shí)際上是對CFRunloopTimerRef的封裝;runloop啟動時(shí)設(shè)置的最大超時(shí)時(shí)間實(shí)際上是GCD的dispatch_source_t類型。
數(shù)據(jù)結(jié)構(gòu):
// Timer:interval:(鬧鐘間隔), tolerance:(延期時(shí)間容忍度),callout(回調(diào)函數(shù))CFRunLoopTimer {firing =..., interval = ...,tolerance = ...,next fire date = ...,callout = ...}
創(chuàng)建與生效;
//NSTimer: // 創(chuàng)建一個(gè)定時(shí)器(需要手動加到runloop的mode中) + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo; // 默認(rèn)已經(jīng)添加到主線程的runLoop的DefaultMode中 + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;// performSEL方法// 內(nèi)部會創(chuàng)建一個(gè)Timer到當(dāng)前線程的runloop中(如果當(dāng)前線程沒runloop則方法無效;performSelector:onThread: 方法放到指定線程runloop中)- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
相關(guān)類型(GCD的timer與CADisplayLink)
GCD的timer:dispatch_source_t 類型,可以精確的參數(shù),不用以來runloop和mode,性能消耗更小。
dispatch_source_set_timer(dispatch_source_t source, // 定時(shí)器對象 dispatch_time_t start, // 定時(shí)器開始執(zhí)行的時(shí)間 uint64_t interval, // 定時(shí)器的間隔時(shí)間 uint64_t leeway // 定時(shí)器的精度 );