NSRunLoop:
OSX/iOS 系統(tǒng)中,提供了兩個(gè)這樣的對(duì)象: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對(duì)應(yīng)一個(gè)線程他們是一一對(duì)應(yīng)的關(guān)系拂檩,關(guān)系保存在一個(gè)全局的Dictionary里邊侮腹,線程剛創(chuàng)建時(shí)沒有RunLoop,如果不主動(dòng)獲取,是不會(huì)有的稻励,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è)線程只能有唯一對(duì)應(yīng)的runloop斟览;但這個(gè)根runloop里可以嵌套子runloops;
自動(dòng)釋放池寄生于Runloop:程序啟動(dòng)后辑奈,主線程注冊(cè)了兩個(gè)Observer監(jiān)聽runloop的進(jìn)出與睡覺苛茂。一個(gè)最高優(yōu)先級(jí)OB監(jiān)測(cè)Entry狀態(tài);一個(gè)最低優(yōu)先級(jí)OB監(jiān)聽BeforeWaiting狀態(tài)和Exit狀態(tài)鸠窗。
線程(創(chuàng)建)-->runloop將進(jìn)入-->最高優(yōu)先級(jí)OB創(chuàng)建釋放池-->runloop將睡-->最低優(yōu)先級(jí)OB銷毀舊池創(chuàng)建新池-->runloop將退出-->最低優(yōu)先級(jí)OB銷毀新池-->線程(銷毀)
RunLoop只能運(yùn)行在一種mode下妓羊,如果要換mode當(dāng)前的loop也需要停下重啟成新的。利用這個(gè)機(jī)制塌鸯,ScrollView過程中NSDefaultRunLoopMode的mode會(huì)切換UITrackingRunLoopMode來保證ScrollView的流暢滑動(dòng)不受只能在NSDefaultRunLoopMode時(shí)處理的事件影響滑動(dòng)侍瑟。同時(shí)mode還是可定制的。
NSDefaultRunLoopMode:默認(rèn)丙猬,空閑狀態(tài)
UITrackingRunLoopMode:ScrollView滑動(dòng)時(shí)
UIInitializationRunLoopMode:?jiǎn)?dòng)時(shí)
NSRunLoopCommonModes:Mode集合 Timer計(jì)時(shí)會(huì)被scrollView的滑動(dòng)影響的問題可以通過將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 對(duì)應(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);
/// 注冊(cè)一個(gè)回調(diào)揪垄,當(dāng)線程銷毀時(shí)穷吮,順便也銷毀其對(duì)應(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í)際上是對(duì)CFRunloopTimerRef的封裝饥努;runloop啟動(dòng)時(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í)器(需要手動(dòng)加到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)部會(huì)創(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í)器對(duì)象 dispatch_time_t start, // 定時(shí)器開始執(zhí)行的時(shí)間 uint64_t interval, // 定時(shí)器的間隔時(shí)間 uint64_t leeway // 定時(shí)器的精度 );