iOS核心筆記——RunLoop-基礎

來源:互聯網
上載者:User

標籤:timer   rac   spinlock   ini   default   pool   接收   載入   pex   

1、RunLoop介紹:

?瞭解:RunLoop從字面意思看就是運行迴圈跑圈,通常情況下,一個線程一次只能執行一個任務;任務執行完畢後線程就會進入消亡狀態隨之退出。有時候我們希望線程執行完任務之後還能隨時處理事件且不退出,所以,iOS提供了RunLoop。

1-1、什麼是RunLoop?

?重要:RunLoop實際上就是一個對象,RunLoop對象管理其需要處理的事件和訊息;RunLoop能夠讓線程在沒有處理訊息時進入休眠狀態以避免佔用資源、在監聽到事件來源發送的訊息時立刻喚醒線程。RunLoop提供了一個入口函數來實現運行迴圈,當線程執行該函數後,便會處於接收訊息->等待處理->處理的迴圈中,直到運行迴圈結束、RunLoop退出;RunLoop對象也隨之銷毀。

RunLoop迴圈邏輯:

?重要:在iOS程式中,mian函數中的UIApplicationMain()函數內部啟動了一個與主線程相關聯的RunLoop;所以,UIApplicationMain()函數一直沒有返回;保持程式持續運行。

RunLoop實現原理:

1-2、RunLoop對象:

?瞭解:蘋果不允許我們直接通過alloc、init方式建立線程中的RunLoop對象,只有在第一次訪問線程中的RunLoop對象時,RunLoop對象將以懶載入的形式建立,所以,iOS中提供了2套API來訪問和使用RunLoop。

1、Foundation架構:
  1. NSRunLoop:在Foundation架構中,一個NSRunLoop對象就代表了一個線程中的運行迴圈。
  2. 擷取方式
類型 方式 備忘
當前線程 通過NSRunLoop的currentRunLoop類方法便能擷取到當前線程中的RunLoop對象 當前RunLoop對象可以是主線程中的RunLoop對象,也可以是子線程中的RunLoop對象,如果當前為子線程,則懶載入RunLoop對象之後需要手動調用run方法開啟運行迴圈
主線程 通過NSRunLoop的mainRunLoop類方法能夠擷取到主線程中的RunLoop對象 噹噹前線程為主線程時,mainRunLoop與currentRunLoop擷取到的是同一個對象
2、Core Foundation架構:
  1. CFRunLoopRef:Core Foundation架構中,一個CFRunLoopRef對象就代表線程中的運行迴圈。
  2. 擷取方式
類型 方式 備忘
主線程 CFRunLoopGetMain()函數 擷取主線程中的RunLoop對象,程式運行時,自動建立並開啟
當前線程 CFRunLoopGetCurrent()函數 擷取當前線程中的RunLoop對象,如果當前為子線程,則懶載入RunLoop對象之後需要手動調用run方法開啟運行迴圈
1-3、RunLoop與線程:
1.    /// 全域的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef  
2. static CFMutableDictionaryRef loopsDic;
3. /// 訪問 loopsDic 時的鎖
4. static CFSpinLock_t loopsLock;
5.
6. /// 擷取一個 pthread 對應的 RunLoop。
7. CFRunLoopRef_CFRunLoopGet(pthread_t thread){
8. OSSpinLock Lock(&loopsLock);
9.
10. if(!loopsDic){
11. // 第一次進入時,初始化全域Dic,並先為主線程建立一個 RunLoop。
12. loopsDic = CFDictionaryCreateMutable();
13. CFRunLoopRef mainLoop = _CFRunLoopCreate();
14. CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
15. }
16.
17. /// 直接從 Dictionary 裡擷取。
18. CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread);
19.
20. if(!loop){
21. /// 取不到時,建立一個
22. loop = _CFRunLoopCreate();
23. CFDictionarySetValue(loopsDic, thread, loop);
24. /// 註冊一個回調,當線程銷毀時,順便也銷毀其對應的 RunLoop。
25. _CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
26. }
27.
28. OSSpinLockUnLock(&loopsLock);
29. returnloop;
30. }
31.
32. CFRunLoopRef CFRunLoopGetMain(){
33. return _CFRunLoopGet(pthread_main_thread_np());
34. }
35.
36. CFRunLoopRef CFRunLoopGetCurrent(){
37. return _CFRunLoopGet(pthread_self());
38. }

?重要: CFRunLoop是基於pthread來管理的,線程和RunLoop之間是一一對應的,其關係是儲存在一個全域的Dictionary裡。線程剛建立時並沒有RunLoop,如果你不主動擷取,那它一直都不會有。RunLoop 的建立是發生在第一次擷取時,RunLoop 的銷毀是發生線上程結束時。只能在一個線程的內部擷取其 RunLoop(主線程除外)。

1-4、主線程與子線程:

?重要:在子線程中使用RunLoop時,最好在子線程中的任務中包一個自動釋放池(即:在子線程中的任務中手動添加@autoreleasepool,將任務包裹起來,避免記憶體泄露)。

1-5、RunLoop注意事項:

?瞭解:1、每條線程都有唯一一個與之對應的RunLoop對象;

?瞭解:2、主線程的RunLoop對象在程式啟動時已經自動建立完畢,並自動開啟運行迴圈;子線程的RunLoop對象需要主動訪問其RunLoop對象以懶載入的形式建立,並且,子線程的RunLoop對象需要調用start方法手動開啟;

?瞭解:3、RunLoop在第一次擷取時以懶載入的形式建立,線上程結束時銷毀。

?瞭解:4、RunLoop對象必須依賴線程,即:有線程才有RunLoop對象,沒有線程就沒有RunLoop對象;不能有RunLoop對象而沒有線程。

1-6、RunLoop學習:1、蘋果官方文檔:

RunLoop官方文檔介紹

2、CFRunLoopRef源碼:

CFRunLoop源碼

2、RunLoop對外介面:

?重要:在 CoreFoundation 裡面關於 RunLoop 有5個類:

CFRunLoopRef :RunLoop對象
CFRunLoopModeRef :RunLoop模式
CFRunLoopSourceRef :RunLoop事件來源
CFRunLoopTimerRef :RunLoop定時器
CFRunLoopObserverRef:RunLoop觀察者

其中,CFRunLoopModeRef 類並沒有對外暴露,只是通過 CFRunLoopRef 的介面進行了封裝。它們之間的關係如所示:

2-1、CFRunLoopRef:

?重要:1、一個 RunLoop 包含若干個 Mode,每個 Mode 又包含若干個 Source/Timer/Observer。每次調用 RunLoop 的主函數時,只能指定其中一個 Mode,這個Mode被稱作 CurrentMode。如果需要切換 Mode,只能退出 Loop,再重新指定一個 Mode 進入。這樣做主要是為了分隔開不同組的 Source/Timer/Observer,讓其互不影響。

?重要:2、RunLoop中的Source/Timer/Observer被統稱為mode item,一個 item 可以被同時加入多個mode。但一個 item被重複加入同一個mode時是不會有效果的。如果一個 mode 中一個item都沒有,則RunLoop會直接退出,不進入迴圈。

?瞭解:3、

2-2、CFRunLoopSourceRef:

?重要:CFRunLoopSourceRef是事件產生的地方,Source有兩個版本:Source0 和 Source1。

· Source0 :非基於Post,只包含了一個回調(函數指標),它並不能主動觸發事件。使用時,你需要先調用 CFRunLoopSourceSignal(source),將這個 Source 標記為待處理,然後手動調用 CFRunLoopWakeUp(runloop) 來喚醒 RunLoop,讓其處理這個事件。

· Source1 :基於Port,包含了一個 mach_port 和一個回調(函數指標),被用於通過核心和其他線程相互發送訊息。這種 Source 能主動喚醒 RunLoop 的線程,其原理在下面會講到。

?瞭解:以前CFRunLoopSourceRef可分為:①Port-Based Sources;②Custom Input Sources;③Cocoa Perform Selector Sources

基於事件來源的函數調用棧

2-3、CFRunLoopTimerRef:

?重要:CFRunLoopTimerRef 是基於時間的觸發器,它和 NSTimer是toll-free bridged的,可以混用。其包含一個時間長度和一個回調(函數指標)。當其加入到 RunLoop 時,RunLoop會註冊對應的時間點,當時間點到時,RunLoop會被喚醒以執行那個回調。

2-4、CFRunLoopObserverRef:

?重要:CFRunLoopObserverRef是觀察者,每個Observer都包含了一個回調(函數指標),當RunLoop的狀態發生變化時,觀察者就能通過回調接受到這個變化。可以觀測的時間點有以下幾個:

1.typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
2. kCFRunLoopEntry = (1UL << 0), // 1 --- 即將進入RunLoop
3. kCFRunLoopBeforeTimers = (1UL << 1), // 2 --- 即將處理定時器
4. kCFRunLoopBeforeSources = (1UL << 2), // 4 --- 即將處理Source
5. kCFRunLoopBeforeWaiting = (1UL << 5), // 32 --- 即將進入休眠狀態
6. kCFRunLoopAfterWaiting = (1UL << 6), // 64 --- 喚醒RunLoop
7. kCFRunLoopExit = (1UL << 7), // 128 --- 即將退出RunLoop
8. kCFRunLoopAllActivities = 0x0FFFFFFFU // RunLoop所有狀態
9.};

?重要:給RunLoop對象設定觀察者:

步驟:①建立觀察者對象;②將觀察者對象設定為指定RunLoop對象的觀察者。

3、CFRunLoopModeRef:

?重要:CFRunLoopModeRef代表RunLoop的運行模式,一個 RunLoop 包含若干個 Mode,每個 Mode 又包含若干個 Source/Timer/Observer。每次調用 RunLoop 的主函數時,只能指定其中一個 Mode,這個Mode被稱作 CurrentMode。如果需要切換 Mode,只能退出 Loop,再重新指定一個 Mode 進入。這樣做主要是為了分隔開不同組的 Source/Timer/Observer,讓其互不影響。

3-1、CFRunLoopMode和CFRunLoop的結構大致如下:

CFRunLoopMode結構

1.struct__CFRunLoopMode{  
2. CFStringRef_name; // Mode Name, 例如 @"kCFRunLoopDefaultMode"
3. CFMutableSetRef_sources0; // Set<CFRunLoopSourceRef>
4. CFMutableSetRef_sources1; // Set<CFRunLoopSourceRef>
5. CFMutableArrayRef_observers;// Array<CFRunLoopObserverRef>
6. CFMutableArrayRef_timers; // Array<CFRunLoopTimerRef>
7. ...
8.};

CFRunLoop結構

1.struct__CFRunLoop{  
2. CFMutableSetRef_commonModes; // Set<CFStringRef>
3. CFMutableSetRef_commonModeItems; // Set<Source/Observer/Timer>
4. CFRunLoopModeRef_currentMode; // Current Runloop Mode
5. CFMutableSetRef_modes; // Set<CFRunLoopModeRef>
6. ...
7.};
3-2、RunLoop5種模式:

?重要: “CommonModes”:一個 Mode 可以將自已標幟為”Common”屬性(通過將其 ModeName 添加到 RunLoop 的 “commonModes” 中)。每當 RunLoop 的內容發生變化時,RunLoop 都會自動將 _commonModeItems 裡的 Source/Observer/Timer 同步到具有 “Common” 標記的所有Mode裡。

?瞭解:應用情境舉例:主線程的 RunLoop 裡有兩個預置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。這兩個 Mode 都已經被標記為”Common”屬性。DefaultMode 是 App 平時所處的狀態,TrackingRunLoopMode 是追蹤 ScrollView 滑動時的狀態。當你建立一個 Timer 並加到 DefaultMode 時,Timer 會得到重複回調,但此時滑動一個TableView時,RunLoop 會將 mode 切換為 TrackingRunLoopMode,這時 Timer 就不會被回調,並且也不會影響到滑動操作。有時你需要一個 Timer,在兩個 Mode 中都能得到回調,一種辦法就是將這個 Timer 分別加入這兩個 Mode。還有一種方式,就是將 Timer 加入到頂層的 RunLoop 的 “commonModeItems” 中。”commonModeItems” 被 RunLoop 自動更新到所有具有”Common”屬性的 Mode 裡去。
CFRunLoop對外暴露的管理 Mode 介面只有下面2個:

1.  CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);  
2. CFRunLoopRunInMode(CFStringRef modeName, ...);
3.

Mode 暴露的管理 mode item 的介面有下面幾個:

1.  CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);  
2.
3. CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
4.
5. CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
6.
7. CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
8.
9. CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
10.
11. CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);

?重要:只能通過mode name來操作內部的mode,當傳入一個新的mode name時,但是,RunLoop內部沒有對應mode時,RunLoop會自動幫你建立對應的 CFRunLoopModeRef。對於一個RunLoop來說,其內部的mode只能增加不能刪除。蘋果公開提供的 Mode 有兩個:kCFRunLoopDefaultMode (NSDefaultRunLoopMode)UITrackingRunLoopMode,可以用這兩個Mode Name來操作其對應的Mode。同時蘋果還提供了一個操作 Common 標記的字串:kCFRunLoopCommonModes (NSRunLoopCommonModes),可以用這個字串來操作 Common Items,或標記一個 Mode 為 “Common”。使用時注意區分這個字串和其他 mode name。

3-3、RunLoop模式詳細介紹:

iOS核心筆記——RunLoop-基礎

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.