天天看點

iOS基礎知識4

一、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;
}