RunLoop的基本介紹

RunLoop是iOS開發中的一個基礎概念,一個程序運行后,你動則它動,你不動它不動,這種時刻待命的效果,就是RunLoop的作用了。
首先什么是RunLoop,它的字面意思是一個運行循環,用來保持程序的運行,處理APP中的各種事件(如觸摸,定時器等),可以提高程序性能,達到勞逸結合。iOS中提供了NSRunLoopCFRunLoopRef兩個對象來訪問和使用RunLoop。
每條線程都有唯一的一個與之對應的RunLoop對象。主線程的RunLoop已經自動創建好了,子線程的RunLoop需要主動創建。RunLoop在第一次獲取時創建,在線程結束時銷毀,因為該方法本身是懶加載,如果第一次調用該方法,那么會創建子線程對應的runloop并使用字典把線程對象和runloop保存起來,后面調用的時候就直接取值。

// 獲得當前線程的RunLoop對象
[NSRunLoop currentRunLoop];
// 獲得主線程的RunLoop對象
[NSRunLoop mainRunLoop];

// 獲得當前線程的RunLoop對象
CFRunLoop GetCurrent();
// 獲得主線程的RunLoop對象
CFRunLoop GetMain();

蘋果官方文檔
 https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html

Core Foundation中關于RunLoop的共有5個類

  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef

下面我們來了解一下它們的特點。
其中CFRunLoopModeRef類并沒有對外暴露,只是通過 CFRunLoopRef 的接口進行了封裝,它代表RunLoop的運行模式。
1.CFRunLoopModeRef代表RunLoop的運行模式
2.每次RunLoop啟動時,只能指定其中一個 Mode,這個Mode被稱作 CurrentMode
3.如果需要切換Mode,只能退出Loop,再重新指定一個Mode進入
4.這樣做主要是為了分隔開不同組的Source/Timer/Observer,讓其互不影響
5.一個 RunLoop 包含若干個 Mode,每個Mode又包含若干個Source/Timer/Observer
6.RunLoop中如果沒有事件源,即沒有Source也沒有Timer,那么它一啟動就會退出。

系統為我們默認注冊了5個Mode:
1.kCFRunLoopDefaultMode:App的默認Mode,通常主線程是在這個Mode下運行
2.UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響
3.UIInitializationRunLoopMode: 在剛啟動 App 時第進入的第一個 Mode,啟動完成后就不再使用
4.GSEventReceiveRunLoopMode: 接受系統事件的內部 Mode,通常用不到
5.kCFRunLoopCommonModes: 這是一個占位用的Mode,不是一種真正的Mode

有一個比較常見的問題,實現輪播效果時,有時候會出現NSTimer不好用的問題。其內部原因是因為,拖拽scrollView時RunLoop會切換到UITrackingRunloopMode的界面追蹤模式,而我們的定時器是添加到kCFRunDefaultMode模式下的,所以才導致這個問題的出現。我們在這里只要使用NSRunLoopCommonModes模式,把定時器添加到這個模式下,就能避免這個問題。因為把timer添加到被標記為NSRunLoopCommonModes的運行模式下,這個模式默認包括tracking和defaul兩種。


Snip20161216_1.png

**CFRunLoopSourceRef **是事件產生的地方,現在有兩種劃分方式:
Source0:event事件,只含有回調,需要標記待處理(signal),然后手動將runloop喚醒(wakeup)
Source1:包含一個 mach_port 和一個回調,被用于通過內核和其他線程發送的消息,能主動喚醒runloop

Snip20161216_2.png

(以前的劃分方式為:port,custom,performSelector)

CFRunLoopTimerRef是基于時間的觸發器
NSTimer和performSEL方法實際上是對CFRunloopTimerRef的封裝

CFRunLoopObserverRef是用來監聽RunLoop的狀態改變的,一般是以下幾種:

// 進入runloop
kCFRunLoopEntry = (1UL << 0)
// 即將處理time事件
kCFRunLoopBeforeTimers = (1UL << 1)
// 即將處理source事件
kCFRunLoopBeforeSources = (1UL << 2)
// 即將休眠
kCFRunLoopBeforeWaiting = (1UL << 5)
// runloop被喚醒
kCFRunLoopAfterWaiting = (1UL << 6)
// runloop退出
kCFRunLoopExit = (1UL << 7)

每個 Observer 都包含了一個回調(函數指針),當 RunLoop 的狀態發生變化時,觀察者就能通過回調接受到這個變化。
如圖為RunLoop的邏輯處理過程:

Snip20161216_3.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容