一、RunLoop和線程有什麼關系?
每個線程,包括主線程(main thread),都有與之對應的RunLoop對象。
主線程的RunLoop是預設啟動的,子線程的RunLoop預設是不開啟的,需要手動開啟子線程的RunLoop。
iOS程式裡面,程式啟動後會有這樣的一個main()函數:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil,
NSStringFromClass([AppDelegate class]));
}
}
這是主循環,保證我們的程式一直運作下去。
UIApplicationMain()函數,這個方法會為main thread設定一個NSRunLoop對象,這就解釋了:為什麼我們的應用可以在無人操作的時候休息,需要讓它幹活的時候又能立馬響應。
對其它線程來說,RunLoop預設是沒有啟動的,如果你需要更多的線程互動則可以手動配置和啟動,如果線程隻是去執行一個長時間的已确定的任務則不需要。
在任何一個Cocoa程式的線程中,都可以通過以下代碼來擷取到目前線程的RunLoop。
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
RunLoop在執行完畢後,就會進入休眠,隻有在某個情況觸發了,RunLoop才會繼續被調用。
二、RunLoop的mode作用是什麼?
Mode主要是用來指定事件在運作循環中得優先級,有:
1、NSDefaultRunLoopMode(KCFRunLoopDefaultMode) -> 預設
2、UITrackingRunLoopMode:ScrollView滑動時會切換到該Mode
3、UIInitializationRunLoopMode -> Run Loop啟動時,會切換到該Mode
4、NSRunLoopCommonModes(KCFRunLoopCommonModes) -> Mode集合
蘋果公開的Mode有兩個:
NSDefaultRunLoopMode(KCFRunLoopDefaultMode)和NSRunLoopCommonModes(KCFRunLoopCommonModes)
在程式設計中如果我們把一個NSTimer對象以NSDefaultRunLoopMode(KCFRunLoopDefaultMode)添加到主運作循環中,ScrollView滾動過程中會因為Mode的切換,而導緻NSTimer将不再被排程。當我們滾動的時候,也希望不排程,那就應該使用預設模式。但是,如果我們希望ScrollView在滾動時,計時器也要回調,那就應該使用NSRunLoopCommonModes(KCFRunLoopCommonModes)。
三、以+scheduledTimerWithTimeInterval...的方式觸發的timer,在滑動頁面上的清單時,timer會暫定回調,為什麼?如何解決?
RunLoop隻能運作在一種mode下,如果要換mode,目前的loop也需要停下來重新開機成新的mode。ScrollView滾動過程中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode會切換到UITrackingRunLoopMode來保證ScrollView的流暢滑動。
如果我們把一個NSTimer對象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主運作循環中的時候,ScrollView滾動過程中會因為mode的切換,而導緻NSTimer将不再被排程。
是以:
Timer計時會被scrollView的滑動影響的問題可以通過将timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)來解決。
四、猜想RunLoop内部是如何實作的?
本質:内部就是 do-while 循環,在這個循環内部不斷地處理各種事件(任務),比如:Source、Timer、Observer。
每條線程都有唯一一個RunLoop對象與之對應,主線程的RunLoop預設已經啟動 。
子線程的RunLoop需要手動啟動。
每次RunLoop啟動時,隻能指定其中一個mode,這個mode被稱作currentMode。
如果需要切換mode,隻能退出loop,再重新指定一個mode進入,這樣做主要是為了隔離不同mode中的Source、Timer、Observer,讓其互不影響。
一般來講,一個線程一次隻能執行一個任務,執行完成後線程就會退出。如果我們需要一個機制,讓線程能随時處理事件但并不退出,通常的代碼邏輯是這樣的:
function loop() {
initialize();
do {
var message = get_next_message();
process_message(message);
} while (message != quit);
}
僞代碼while循環:
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
int main(int argc, char * argv[]) {
//程式一直運作狀态
while (AppIsRunning) {
//睡眠狀态,等待喚醒事件
id whoWakesMe = SleepForWakingUp();
//得到喚醒事件
id event = GetEvent(whoWakesMe);
//開始處理事件
HandleEvent(event);
}
return 0;
}