




已閱讀5頁,還剩4頁未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
RunLoop總結(jié):RunLoop基礎(chǔ)知識(shí)什么是RunLoop?顧名思義,它就是一個(gè)運(yùn)行循環(huán)。一個(gè)RunLoop 就是一個(gè)用于處理既定工作和接收到的外來事件的事件處理循環(huán)。RunLoop的存在目的就是當(dāng)線程中有任務(wù)時(shí),保證線程忙著干活;當(dāng)線程中沒有任務(wù)時(shí),讓線程睡眠,以節(jié)省資料(想想看,你是在房間里一直轉(zhuǎn)圈抗餓還是躺在床上睡覺更抗餓?)。 理解了 EventLoop 就 能很好的理解RunLoop了。 簡單的用偽代碼來表示就是這樣的:function loop() initialize(); while (message != quit) var message = get_next_message(); process_message(message); 關(guān)于RunLoop,蘋果的Cocoa 和 CoreFoundation 框架都分別提供了NSRunLoop 和 CFRunLoopRef供開發(fā)者調(diào)用和執(zhí)行操作。 CFRunLoopRef 只是一個(gè)結(jié)構(gòu)體,而 NSRunLoop是一個(gè)NSObject 對(duì)象,必然是蘋果將 CFRunLoopRef進(jìn)行了封裝。 需要注意的是NSRunLoop并不是線程安全的,而 CFRunLoopRef 是線程安全的。 官方文檔原文是:Thread safety varies depending on which API you are using to manipulate your run loop. The functions in Core Foundation are generally thread-safe and can be called from any thread. If you are performing operations that alter the configuration of the run loop, however, it is still good practice to do so from the thread that owns the run loop whenever possible.The Cocoa NSRunLoop class is not as inherently thread safe as its Core Foundation counterpart. If you are using the NSRunLoop class to modify your run loop, you should do so only from the same thread that owns that run loop. Adding an input source or timer to a run loop belonging to a different thread could cause your code to crash or behave in an unexpected way.接下來,看一下CFRunLoopRef里都保存了哪些數(shù)據(jù)?可以從CF框架源碼 的 CFRunLoop.h和CFRunLoop.c,看看 蘋果對(duì) CFRunLoopRef 的定義。 CFRunLoopRef是 結(jié)構(gòu)體_CFRunLoop *的重命名,由 typedef struct _CFRunLoop * CFRunLoopRef; 可知; _CFRunLoop 的定義:struct _CFRunLoop CFRuntimeBase _base; pthread_mutex_t _lock; /* locked for accessing mode list,每次讀取mode list 要加鎖 */ _CFPort _wakeUpPort; / used for CFRunLoopWakeUp Boolean _unused; volatile _per_run_data *_perRunData; / reset for runs of the run loop pthread_t _pthread; /與該runLoop 關(guān)聯(lián)的線程 uint32_t _winthread; CFMutableSetRef _commonModes; / set 中保存的就是 NSRunLoopCommonModes表示的mode,我們也可以將自定義的mode 添加到這個(gè)set 里。 CFMutableSetRef _commonModeItems; /添加到NSRunLoopCommonModes中的source/timer等item 都會(huì)被添加到這個(gè)set里,這在應(yīng)用場景一中有打印出來。 CFRunLoopModeRef _currentMode; /RunLoop 當(dāng)前執(zhí)行的是哪個(gè)mode CFMutableSetRef _modes; / 該runLoop 中所有的mode struct _block_item *_blocks_head; struct _block_item *_blocks_tail; CFAbsoluteTime _runTime; CFAbsoluteTime _sleepTime; CFTypeRef _counterpart;再來看一下RunLoopMode 的結(jié)構(gòu),之前說過RunLoopMode 中存放的是兩種source/timer/observer,而 CFRunLoopModeRef是struct _CFRunLoopMode *重命名的(typedef struct _CFRunLoopMode *CFRunLoopModeRef;), 看下定義就明白了:struct _CFRunLoopMode CFRuntimeBase _base; pthread_mutex_t _lock; /* must have the run loop locked before locking this */ CFStringRef _name; /mode 的name Boolean _stopped; char _padding3; CFMutableSetRef _sources0; / 保存所有source0 的set CFMutableSetRef _sources1; / 保存所有source1 的set CFMutableArrayRef _observers; / 保存所有observer 的數(shù)組 CFMutableArrayRef _timers; / 保存所有timer 的數(shù)組 CFMutableDictionaryRef _portToV1SourceMap; _CFPortSet _portSet; CFIndex _observerMask;#if USE_DISPATCH_SOURCE_FOR_TIMERS dispatch_source_t _timerSource; dispatch_queue_t _queue; Boolean _timerFired; / set to true by the source when a timer has fired Boolean _dispatchTimerArmed;#endif#if USE_MK_TIMER_TOO mach_port_t _timerPort; Boolean _mkTimerArmed;#endif#if DEPLOYMENT_TARGET_WINDOWS DWORD _msgQMask; void (*_msgPump)(void);#endif uint64_t _timerSoftDeadline; /* TSR */ uint64_t _timerHardDeadline; /* TSR */;看完上面 _CFRunLoopMode 和 _CFRunLoop的定義,關(guān)于 RunLoop 中保存的是RunLoopMode,而RunLoopMode中保存的才是實(shí)際的任務(wù)這點(diǎn)沒有疑問了。如何創(chuàng)建一個(gè)RunLoop?包括MainRunLoop在內(nèi),每一個(gè)RunLoop都與一個(gè)線程關(guān)聯(lián)著。確切的說,是先有線程,再有RunLoop。 關(guān)于線程與RunLoop的關(guān)系,在RunLoop官方文檔的第一節(jié)講的很清楚。 我們不用,也最好不要顯示的創(chuàng)建RunLoop,蘋果提供了兩個(gè)API,便于我們來獲取RunLoop。 CFRunLoopGetMain() 和 CFRunLoopGetCurrent(),分別用于獲取MainRunLoop和當(dāng)前線程的RunLoop(在主線程中調(diào)用CFRunLoopGetCurrent()與CFRunLoopGetMain()獲取的其實(shí)都是MainRunLoop)。先來看一下,這兩個(gè)函數(shù)的源碼實(shí)現(xiàn):CFRunLoopRef CFRunLoopGetMain(void) CHECK_FOR_FORK(); static CFRunLoopRef _main = NULL; / no retain needed /通過_CFRunLoopGet0 這個(gè)關(guān)鍵函數(shù),取出MainRunLoop。 if (!_main) _main = _CFRunLoopGet0(pthread_main_thread_np(); / no CAS needed return _main;CFRunLoopRef CFRunLoopGetCurrent(void) CHECK_FOR_FORK(); CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(_CFTSDKeyRunLoop); if (rl) return rl; /通過_CFRunLoopGet0 這個(gè)關(guān)鍵函數(shù),取出當(dāng)前RunLoop。 return _CFRunLoopGet0(pthread_self();從以上源碼,可以看出RunLoop 是通過 _CFRunLoopGet0函數(shù)來獲取的,并且以線程作為參數(shù)。 這個(gè)函數(shù)的作用與 通過 key 從 NSDictionary 獲取Value 極為相似。接下來,看一下 _CFRunLoopGet0 的實(shí)現(xiàn)(太長不想看,可以看下面的偽代碼):static CFMutableDictionaryRef _CFRunLoops = NULL;static CFLock_t loopsLock = CFLockInit;/ should only be called by Foundation/ t=0 is a onym for main thread that always worksCF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) if (pthread_equal(t, kNilPthreadT) t = pthread_main_thread_np(); _CFLock(&loopsLock); if (!_CFRunLoops) _CFUnlock(&loopsLock); CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); CFRunLoopRef mainLoop = _CFRunLoopCreate(pthread_main_thread_np(); CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np(), mainLoop); if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&_CFRunLoops) CFRelease(dict); CFRelease(mainLoop); _CFLock(&loopsLock); CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(_CFRunLoops, pthreadPointer(t); _CFUnlock(&loopsLock); if (!loop) CFRunLoopRef newLoop = _CFRunLoopCreate(t); _CFLock(&loopsLock); loop = (CFRunLoopRef)CFDictionaryGetValue(_CFRunLoops, pthreadPointer(t); if (!loop) CFDictionarySetValue(_CFRunLoops, pthreadPointer(t), newLoop); loop = newLoop; / dont 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;如果上面的源碼看不懂,那就來看一下簡化后的偽代碼:/ 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRefstatic 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è),一定要傳一個(gè)線程參數(shù) loop = _CFRunLoopCreate(thread); CFDictionarySetValue(loopsDic, thread, loop); / 注冊一個(gè)回調(diào),當(dāng)線程銷毀時(shí),順便也銷毀其對(duì)應(yīng)的 RunLoop。 _CFSetTSD(., thread, loop, _CFFinalizeRunLoop); OSSpinLockUnLock(&loopsLock); return loop;大致過程,獲取某個(gè)線程的RunLoop,首先以 線程作為key,從全局字典中找,如果沒找到,則新建一個(gè),并以線程為key,RunLoop為Value 存到全局字典中(如果全局字典不存在,就先初始化全局字典,并新建一個(gè)MainRunLoop 保存到全局字典中)。我們自己在使用RunLoop時(shí),可能比較多的是用 NSRunLoop,所以與此相關(guān)的API其實(shí)就兩個(gè):/ 往RunLoop 的 Mode 中添加一個(gè)timer- (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode;/ 往RunLoop的 Mode 中添加一個(gè)source1任務(wù)- (void)addPort:(NSPort *)aPort forMode:(NSRunLoopMode)mode;/ 從RunLoop的 Mode 里刪除source1 任務(wù)- (void)removePort:(NSPort *)aPort forMode:(NSRunLoopMode)mode;如果使用CFRunLoopRef,那么常用的API也就多了幾個(gè)而已:CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);蘋果公開提供的 Mode 有兩個(gè):kCFRunLoopDefaultMode (NSDefaultRunLoopMode) 和 UITrackingRunLoopMode,你可以用這兩個(gè) Mode Name 來操作其對(duì)應(yīng)的 Mode。如果要操作多個(gè)Mode,我們可以使用 kCFRunLoopCommonModes(NSRunLoopCommonModes),當(dāng)然我們可以把自定義的Mode 添加都CommonModes中,可以使用如下的API來操作:CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);接下來就是重點(diǎn)了,RunLoop是內(nèi)部是如何來執(zhí)行任務(wù)的?CFRunLoopRun 和 CFRunLoopRunInMode 內(nèi)部都調(diào)用了 CFRunLoopRunSpecific。而 CFRunLoopRunSpecific 內(nèi)部又調(diào)用了 _CFRunLoopRun, CFRunLoopRunSpecific 和 _CFRunLoopRun 合起來就是RunLoop的完整實(shí)現(xiàn)了。如下:/ RunLoop的實(shí)現(xiàn)int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) / 首先根據(jù)modeName找到對(duì)應(yīng)mode CFRunLoopModeRef currentMode = _CFRunLoopFindMode(runloop, modeName, false); / 如果mode里沒有ce/timer/observer, 直接返回。 if (_CFRunLoopModeIsEmpty(currentMode) return; / 1. 通知 Observers: RunLoop 即將進(jìn)入 loop。 _CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry); / 內(nèi)部函數(shù),進(jìn)入loop _CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) Boolean sourceHandledThisLoop = NO; int retVal = 0; do / 2. 通知 Observers: RunLoop 即將觸發(fā) Timer 回調(diào)。 _CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers); / 3. 通知 Observers: RunLoop 即將觸發(fā) Source0 (非port) 回調(diào)。 _CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources); / 執(zhí)行被加入的block _CFRunLoopDoBlocks(runloop, currentMode); / 4. RunLoop 觸發(fā) Source0 (非port) 回調(diào)。 sourceHandledThisLoop = _CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle); / 執(zhí)行被加入的block _CFRunLoopDoBlocks(runloop, currentMode); / 5. 如果有 Source1 (基于port) 處于 ready 狀態(tài),直接處理這個(gè) Source1 然后跳轉(zhuǎn)去處理消息。 if (_Source0DidDispatchPortLastTime) Boolean hasMsg = _CFRunLoopServiceMachPort(dispatchPort, &msg) if (hasMsg) goto handle_msg; / 通知 Observers: RunLoop 的線程即將進(jìn)入休眠(sleep)。 if (!sourceHandledThisLoop) _CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting); / 7. 調(diào)用 mach_msg 等待接受 mach_port 的消息。線程將進(jìn)入休眠, 直到被下面某一個(gè)事件喚醒。 / 一個(gè)基于 port 的Source 的事件。 / 一個(gè) Timer 到時(shí)間了 / RunLoop 自身的超時(shí)時(shí)間到了 / 被其他什么調(diào)用者手動(dòng)喚醒 _CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) mach_msg(msg, MACH_RCV_MSG, port); / thread wait for receive msg / 8. 通知 Observers: RunLoop 的線程剛剛被喚醒了。 _CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting); / 收到消息,處理消息。 handle_msg: / 9.1 如果一個(gè) Timer 到時(shí)間了,觸發(fā)這個(gè)Timer的回調(diào)。 if (msg_is_timer) _CFRunLoopDoTimers(runloop, currentM
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 腸梗阻健康課件
- 肝膽病中醫(yī)診治技術(shù)課件
- 安全聯(lián)鎖培訓(xùn)課件模板
- 高考遼寧真題數(shù)學(xué)試卷
- 拱墅區(qū)小升初數(shù)學(xué)試卷
- 典中點(diǎn)魯教版五四制數(shù)學(xué)試卷
- 高三歷年高考數(shù)學(xué)試卷
- 二年級(jí)上學(xué)期期中數(shù)學(xué)試卷
- 高一一學(xué)期數(shù)學(xué)試卷
- 2025年03月浙江臺(tái)州市立醫(yī)院招聘高層次(博士高級(jí)職稱)衛(wèi)技人員15人筆試歷年專業(yè)考點(diǎn)(難、易錯(cuò)點(diǎn))附帶答案詳解
- GB/T 18391.6-2009信息技術(shù)元數(shù)據(jù)注冊系統(tǒng)(MDR)第6部分:注冊
- GB/T 14501.6-2008六氟化鈾分析方法第6部分:鈾的測定
- 日立電梯MCA調(diào)試培訓(xùn)課件
- 國道市政化改造示范段交通疏解方案
- 2023年遼寧省農(nóng)業(yè)信貸融資擔(dān)保有限責(zé)任公司招聘筆試題庫及答案解析
- 光學(xué)玻璃課件
- 智力殘疾康復(fù)訓(xùn)練課件
- 焊接過程及記錄與檢查表
- 鑄造作業(yè)指導(dǎo)書
- 三菱電機(jī)FX-PLC自動(dòng)化培訓(xùn)課件(完整版)
- (完整版)全國校園籃球特色學(xué)校申報(bào)材料
評(píng)論
0/150
提交評(píng)論