寫這篇文章開始之前棉胀,我都不知道runloop是什么東西,如果從字面的意思翻譯應(yīng)該是一直循環(huán)的跑法瑟,懷疑可能和死鎖有關(guān)系,可是死鎖具體是怎么回事唁奢,我只是記得有這個說法霎挟,也發(fā)現(xiàn)了一個自己不懂的知識。
初識runloop
我在網(wǎng)上看了一下@sunnnyxx 關(guān)于runloop的視頻.了解了一下runloop相關(guān)知識,也去網(wǎng)絡(luò)上看各種關(guān)于runloop的講述麻掸。
我們一般程序就是執(zhí)行一個線程酥夭,是一條直線.有起點(diǎn)終點(diǎn).而runloop就是一直在線程上面畫圓圈,一直在跑圈脊奋,除非切斷否則一直在運(yùn)行熬北。網(wǎng)上說的比喻很好,直線就像曇花一現(xiàn)一樣诚隙,圓就像OS,一直運(yùn)行直到你關(guān)機(jī)為止讶隐。
在我們學(xué)習(xí)iOS生命周期里面都會存在銷毀的過程,但是屏幕好像一直能接收各種指令最楷,感覺很像runloop的功效,好像這些是和頂層UIKit無關(guān),IOS架構(gòu)最底層是Core OS,我分析應(yīng)該是蘋果封裝好了整份,只是我們看不到源碼而已待错。
為什么要使用runloop
@sunnnyxx 在視頻介紹了四個作用:
- 使程序一直運(yùn)行接受用戶輸入
- 決定程序在何時應(yīng)該處理哪些Event
- 調(diào)用解耦(對于編程經(jīng)驗為0的完全沒搞懂這個意思,解釋為Message Queue)
- 節(jié)省CPU時間
這段視頻我覺得不太適合小白去看,因為好多概念還沒有融會貫通烈评,也沒有理解透徹火俄。但是既然看了,就得總結(jié)一下讲冠,至少產(chǎn)生一個樹突先瓜客,留下一個問號,未來把問號變成嘆號竿开。
回到開始的疑問,為什么要使用RunLoop,一般情況下我們是沒必要去啟動線程的RunLoop谱仪,除非需要在一個單獨(dú)的線程長久的檢測某個事件,就像視頻里面提到的類似微信的語音功能否彩,見一個RunLoop專門負(fù)責(zé)監(jiān)聽說話的線程疯攒。看需求而定了列荔。
CFRunLoopSource
Source是RunLoop的數(shù)據(jù)源抽象類,類似IOS中的protocol
RunLoop定義兩個Version的Source
- Source0:處理App內(nèi)部事件,App自己負(fù)責(zé)管理(觸發(fā)),如UIEvent,CFSocket
- Source1:由RunLoop和內(nèi)核管理,Mach port驅(qū)動 如CFMach敬尺、CFMessage
CFRunLoopObserver
向內(nèi)部報告RunLoop當(dāng)前狀態(tài)的更改 CAAnimation
RunLoopObserver 與 Autorelease Pool
UIKit通過RunLoopObserver在RunLoop兩次Sleep間對AutoreleasePool進(jìn)行pop和push,將這次Loop中產(chǎn)生的Autorelease對象釋放。(好像swift中沒有關(guān)于釋放的問題)
CFRunLoopMode
RunLoop在同一時段只能且必須在一種特定Mode下Run
更換Mode時, 需要暫停當(dāng)前的Loop,然后重啟新的Loop
- NSDefalutRunLoopMode? ? ? 默認(rèn)狀態(tài).空閑狀態(tài)
- UITrackingRunLoopMode? ? ?滑動ScrollView
- UIInitializationRunLoopMode? ? 私有,App啟動時
- NSRunLoopCommonModes? ? ?默認(rèn)包括上面第一和第二
UITrackingRunLoopMode 與 NSTimer
默認(rèn)情況下NSTimer被加入NSDefalutRunLoopMode
如果想NSTimer受到組件或者動畫影響 添加到NSRunLoopCommonModes(OC代碼如下:)
[[NSRunLoop currentRunLoop]addTimer:timer...forMode:NSRunLoopCommonModes];
swift版代碼:
NSRunLoop.currentRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
RunLoopMode切換
NSDefaultRunLoopMode->UITrackingRunLoopMode->NSDefalutRunLoopMode
RunLoop的掛起與喚醒
- 制定用于喚醒的
mach_port
端口
- 調(diào)用
mach_msg
監(jiān)聽喚醒端口,被喚醒前,系統(tǒng)內(nèi)核將這個線程掛起,停留在mach_msg_trap
- 由另外一個線程(或另一個進(jìn)程中的某個線程)向內(nèi)核發(fā)送這個端口的msg后,trap狀態(tài)被喚醒,RunLoop繼續(xù)開始干活
AFNetWorking 中創(chuàng)建RunLoop
創(chuàng)建一個常駐服務(wù)線程的很好方法
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefalutRunLoopMode]//一直活著
[runLoop run];
swift版代碼
var loop = NSRunLoop.currentRunLoop()
loop.addPort(NSMachPort(), forMode: NSDefaultRunLoopMode)
loop.run()
一個TableView延遲加載圖片的新思路
[self.avatarImageView performSelector:@selector(serImage:)
withObjetc:downloadedImage
afterDelay:0
inModes:@[NSDefaultRunLoopMode]]
+ (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;
}
這個代碼無法轉(zhuǎn)換成swift贴浙,可能是我沒想到辦法砂吞,大家誰找到了請評論,謝謝了崎溃。
讓Crash的App回光返照 只針對Signal Crash
CFRunLoopRef runloop = CFRunLoopGetCurrent();
NSArray *allModes = CFBridgingRelease(CFRunLoopCopyAllModes(runLoop));
while(1){
for (NSString *mode in allModes){
CFRunLoopInMode((CFStringRef)mode,0.001,false);
}
}
RunLoop事件隊列
每次運(yùn)行run loop蜻直,你線程的run loop對會自動處理之前未處理的消息,并通知相關(guān)的觀察者袁串。具體的順序如下:
- 通知觀察者run loop已經(jīng)啟動
- 通知觀察者任何即將要開始的定時器
- 通知觀察者任何即將啟動的非基于端口的源
- 啟動任何準(zhǔn)備好的非基于端口的源
- 如果基于端口的源準(zhǔn)備好并處于等待狀態(tài)概而,立即啟動;并進(jìn)入步驟9般婆。
- 通知觀察者線程進(jìn)入休眠
- 將線程置于休眠直到任一下面的事件發(fā)生:
- 某一事件到達(dá)基于端口的源
- 定時器啟動
- Run loop設(shè)置的時間已經(jīng)超時
- run loop被顯式喚醒
- 通知觀察者線程將被喚醒到腥。
- 處理未處理的事件
- 如果用戶定義的定時器啟動,處理定時器事件并重啟run loop蔚袍。進(jìn)入步驟2
- 如果輸入源啟動,傳遞相應(yīng)的消息
- 如果run loop被顯式喚醒而且時間還沒超時配名,重啟run loop啤咽。進(jìn)入步驟2
- 通知觀察者run loop結(jié)束。
異步測試
- (BOOL)runUntilBlock:(BOOL(^)())block timeout:(NSTimeInterval)timeout{
__block Boolean fulfilled = NO;
void (^beforeWaiting) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) =
^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
fulfilled = block();
if (fulfilled) {
CFRunLoopStop(CFRunLoopGetCurrent());
}
};
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, 0, beforeWaiting);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
// Run!
CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, false);
CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);
return fulfilled;
}
總結(jié)一下
基本上對于RunLoop渠脉,我只能說我只有簡單的模糊印象宇整,但是不理解,也不太清楚在現(xiàn)實(shí)如何使用芋膘,因為實(shí)踐才能知道如何做鳞青,真的在腦里形成了樹突霸饲,希望在未來搞定,研究透徹臂拓『衤觯看這些代碼時候我發(fā)現(xiàn)我的基礎(chǔ)知識還需要鞏固。感謝不相識的孫源胶惰。
聲明一點(diǎn):不要只去收藏傻工,去看看代碼有沒有問題,知識有沒有解釋錯誤的孵滞,交流學(xué)的更快中捆。