http://www.cocoachina.com/ios/20150601/11970.html
一個(gè) RunLoop 包含若干個(gè) Mode,每個(gè) Mode 又包含若干個(gè) Source/Timer/Observer怠硼。每次調(diào)用 RunLoop 的主函數(shù)時(shí)趾断,只能指定其中一個(gè) Mode,這個(gè)Mode被稱作 CurrentMode增显。如果需要切換 Mode同云,只能退出 Loop,再重新指定一個(gè) Mode 進(jìn)入星澳。這樣做主要是為了分隔開不同組的 Source/Timer/Observer旱易,讓其互不影響。
CFRunLoopSourceRef 是事件產(chǎn)生的地方如暖。Source有兩個(gè)版本:Source0 和 Source1忌堂。
Source0 只包含了一個(gè)回調(diào)(函數(shù)指針),它并不能主動(dòng)觸發(fā)事件枷遂。使用時(shí)棋嘲,你需要先調(diào)用 CFRunLoopSourceSignal(source),將這個(gè) Source 標(biāo)記為待處理封字,然后手動(dòng)調(diào)用 CFRunLoopWakeUp(runloop) 來喚醒 RunLoop,讓其處理這個(gè)事件流妻。
Source1 包含了一個(gè) mach_port 和一個(gè)回調(diào)(函數(shù)指針)绅这,被用于通過內(nèi)核和其他線程相互發(fā)送消息在辆。這種 Source 能主動(dòng)喚醒 RunLoop 的線程度苔,其原理在下面會(huì)講到浑度。
上面的 Source/Timer/Observer 被統(tǒng)稱為 mode item箩张,一個(gè) item 可以被同時(shí)加入多個(gè) mode。但一個(gè) item 被重復(fù)加入同一個(gè) mode 時(shí)是不會(huì)有效果的先慷。如果一個(gè) mode 中一個(gè) item 都沒有,則 RunLoop 會(huì)直接退出福青,不進(jìn)入循環(huán)无午。
你只能通過 mode name 來操作內(nèi)部的 mode誉券,當(dāng)你傳入一個(gè)新的 mode name 但 RunLoop 內(nèi)部沒有對(duì)應(yīng) mode 時(shí),RunLoop會(huì)自動(dòng)幫你創(chuàng)建對(duì)應(yīng)的 CFRunLoopModeRef踊跟。對(duì)于一個(gè) RunLoop 來說商玫,其內(nèi)部的 mode 只能增加不能刪除。
蘋果公開提供的 Mode 有兩個(gè):kCFRunLoopDefaultMode (NSDefaultRunLoopMode) 和 UITrackingRunLoopMode袭异,你可以用這兩個(gè) Mode Name 來操作其對(duì)應(yīng)的 Mode炬藤。
同時(shí)蘋果還提供了一個(gè)操作 Common 標(biāo)記的字符串:kCFRunLoopCommonModes (NSRunLoopCommonModes),你可以用這個(gè)字符串來操作 Common Items上真,或標(biāo)記一個(gè) Mode 為 "Common"羹膳。使用時(shí)注意區(qū)分這個(gè)字符串和其他 mode name。
總結(jié)來說,runMode:beforeDate:
表示的是 runloop 的單次調(diào)用延塑,另外兩者則是循環(huán)調(diào)用。
總的來說,如果你還想從 runloop 里面退出來冕广,就不能用 run
方法。根據(jù)實(shí)踐結(jié)果和文檔沟优,另外兩種啟動(dòng)方法也無法手動(dòng)退出。
這里其實(shí)不正確**runMode:beforeDate: 這個(gè)方法開始的 runloop 其實(shí)是可以手動(dòng)停止的
**
CFRunLoopStop() 方法只會(huì)結(jié)束當(dāng)前的 runMode:beforeDate: 調(diào)用宾肺,而不會(huì)結(jié)束后續(xù)的調(diào)用锨用。
當(dāng)調(diào)用 NSObject 的 performSelecter:afterDelay: 后隘谣,實(shí)際上其內(nèi)部會(huì)創(chuàng)建一個(gè) Timer 并添加到當(dāng)前線程的 RunLoop 中。所以如果當(dāng)前線程沒有 RunLoop掌栅,則這個(gè)方法會(huì)失效码泛。
當(dāng)調(diào)用 performSelector:onThread: 時(shí),實(shí)際上其會(huì)創(chuàng)建一個(gè) Timer 加到對(duì)應(yīng)的線程去晌缘,同樣的痢站,如果對(duì)應(yīng)線程沒有 RunLoop 該方法也會(huì)失效。
RunLoop 啟動(dòng)前內(nèi)部必須要有至少一個(gè) Timer/Observer/Source搀捷,所以 AFNetworking 在 [runLoop run] 之前先創(chuàng)建了一個(gè)新的 NSMachPort 添加進(jìn)去了。通常情況下氢烘,調(diào)用者需要持有這個(gè) NSMachPort (mach_port) 并在外部線程通過這個(gè) port 發(fā)送消息到 loop 內(nèi);但此處添加 port 只是為了讓 RunLoop 不至于退出播玖,并沒有用于實(shí)際的發(fā)送消息蜀踏。
GCD不受RunLoop影響,
普通計(jì)時(shí)器默認(rèn)是被添加到 runloop 的NSDefaultRunLoopMode中的,所以當(dāng)用戶進(jìn)行滑動(dòng)操作的時(shí)候 runloop 切換到UITrackingRunLoopMode,定時(shí)器不在被執(zhí)行
static 的生命周期是整個(gè)工程結(jié)束,如果在方法中聲明那么作用域會(huì)變小,但是不影響其生命周期
這里的 emptyPort 用來維持 runloop 的運(yùn)行掰吕,根據(jù)官方文檔的描述,如果 runloop 中沒有任何 modeItem局待,就不會(huì)啟動(dòng)钳榨,而是立刻退出。之所以選擇作為屬性而不是臨時(shí)變量薛耻,是因?yàn)槲野l(fā)現(xiàn)每次調(diào)用 [NSMachPort port] 方法都會(huì)占用內(nèi)存饼齿,原因暫時(shí)不清楚瘟滨。
NSRunLoop主要是用于objective-c程序,而CFRunLoop主要用于C/C++程序倒淫,這是因?yàn)镃/C++程序無法使用objective-c對(duì)象而創(chuàng)建的一個(gè)類败玉。
**** 所有線程都自動(dòng)創(chuàng)建一個(gè)RunLoop, 在線程內(nèi)通過 [NSRunLoop currentRunLoop] 獲得當(dāng)前線程的RunLoop.****
source0:非基于端口的源
只包含一個(gè)回調(diào)(函數(shù)指針),他不能主動(dòng)觸發(fā)事件
source1:
被用于通過內(nèi)核與其他線程相互發(fā)送消息,能夠主動(dòng)喚醒 RunLoop 的線程
觸摸, 鎖屏, 搖晃 等用戶交互事件
[圖片上傳中返干。。矩欠。(1)]
當(dāng)在其他線程上面執(zhí)行selector時(shí),目標(biāo)線程須有一個(gè)活動(dòng)的run loop躺坟。
cancelPreviousPerformRequestsWithTarget:
cancelPreviousPerformRequestsWithTarget:selector:object:
1.通知觀察者run loop已經(jīng)啟動(dòng)
2.通知觀察者任何即將要開始的定時(shí)器
3.通知觀察者任何即將啟動(dòng)的非基于端口的源
4.處理 ( source0 )
5.如果 source1 準(zhǔn)備好并處于等待狀態(tài)咪橙,立即啟動(dòng)虚倒;并進(jìn)入步驟9。
6.通知觀察者線程進(jìn)入休眠
7.將線程置于休眠直到任一下面的事件發(fā)生:某一事件到達(dá)基于端口的源
定時(shí)器啟動(dòng)
Run loop設(shè)置的時(shí)間已經(jīng)超時(shí)
run loop被顯式喚醒
8.通知觀察者線程將被喚醒菠剩。
9.處理未處理的事件如果用戶定義的定時(shí)器啟動(dòng)赠叼,處理定時(shí)器事件并重啟run loop。進(jìn)入步驟2
如果輸入源啟動(dòng),傳遞相應(yīng)的消息
如果run loop被顯式喚醒而且時(shí)間還沒超時(shí)买鸽,重啟run loop贯被。進(jìn)入步驟2
10.通知觀察者run loop結(jié)束。
Q1: 添加到滑動(dòng) Model 中的計(jì)時(shí)器 普通狀態(tài)下也走
Q2:
2017.2.23號(hào)重新學(xué)習(xí)記錄
線程和 RunLoop 之間是一一對(duì)應(yīng)的看幼,其關(guān)系是保存在一個(gè)全局的 Dictionary 里幌陕。線程剛創(chuàng)建時(shí)并沒有 RunLoop搏熄,如果你不主動(dòng)獲取,那它一直都不會(huì)有宵凌。RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時(shí)止后,RunLoop 的銷毀是發(fā)生在線程結(jié)束時(shí)。你只能在一個(gè)線程的內(nèi)部獲取其 RunLoop(主線程除外)挺益。
一個(gè) RunLoop 包含若干個(gè) Mode矩肩,每個(gè) Mode 又包含若干個(gè) Source/Timer/Observer肃续。每次調(diào)用 RunLoop 的主函數(shù)時(shí)始锚,只能指定其中一個(gè) Mode,這個(gè)Mode被稱作 CurrentMode瞧捌。如果需要切換 Mode姐呐,只能退出 Loop,再重新指定一個(gè) Mode 進(jìn)入头谜。這樣做主要是為了分隔開不同組的 Source/Timer/Observer鸠澈,讓其互不影響笑陈。
這里的 emptyPort 用來維持 runloop 的運(yùn)行,根據(jù)官方文檔的描述乖菱,如果 runloop 中沒有任何 modeItem块请,就不會(huì)啟動(dòng)拳缠,而是立刻退出
這里有個(gè)概念叫 "CommonModes":一個(gè) Mode 可以將自己標(biāo)記為"Common"屬性(通過將其 ModeName 添加到 RunLoop 的 "commonModes" 中)墩新。每當(dāng) RunLoop 的內(nèi)容發(fā)生變化時(shí),RunLoop 都會(huì)自動(dòng)將 _commonModeItems 里的 Source/Observer/Timer 同步到具有 "Common" 標(biāo)記的所有Mode里窟坐。
//將 mode 標(biāo)記為為 "Common"屬性
CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);
"commonModeItems" 被 RunLoop 自動(dòng)更新到所有具有"Common"屬性的 Mode 里去海渊。但是兩個(gè)被標(biāo)記為"Common"的 mode 里面的 item 不會(huì)互相同步
如何切換 runloop 的運(yùn)行 mode
1.先停止 RunLoop
CFRunLoopStop([runloop getCFRunLoop]);
2.再繼續(xù),此時(shí)切換線程
[runloop runMode:@"customMode" beforeDate:[NSDate distantFuture]];
線程通訊 NSMachPort
- Thread ----> RunLoop ----> MachPort
- 想要給某個(gè)線程發(fā)送消息,需要使用該線程的 MachPort
[ remotePort sendBeforeDate:[NSDate date] msgid:kMsg1 components:array from:myPort reserved:0];
[remotePort sendBeforeDate:<#(nonnull NSDate *)#> components:<#(nullable NSMutableArray *)#> from:<#(nullable NSPort *)#> reserved:<#(NSUInteger)#>]