標籤:initial 階段 連接埠 current 程式員 dev 主線程 技術 好的
從接觸iOS到現在也有將近兩年了,對iOS中的RunLoop也有了一定的認識,下面講講個人對RunLoop的理解。
初識RunLoop
RunLoops是與線程相關聯的基礎部分,一個Run Loop就是事件處理迴圈,他是用來調度和協調接收到的事件處理。使用RunLoop的目的,就是使的線程有工作需要做的時候忙碌起來,當沒事做的時候,又可以使得線程休眠。
我們一般程式就是執行一個線程,是一條直線.有起點終點.而runloop就是一直線上程上面畫圓圈,一直在跑圈,除非切斷否則一直在運行。網上說的比喻很好,直線就像曇花一現一樣,圓就像OS,一直運行直到你關機為止。
RunLoop資料
蘋果官方文檔:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html
CFRunLoopRef是開源的:
http://opensource.apple.com/source/CF/CF-1151.16/
RunLoop對象
iOS中有兩套API來訪問和使用RunLoop
- Foundation架構 --> NSRunLoop
- Core Foundation架構 -->CFRunLoopRef
NSRunLoop和CFRunLoopRef都代表著RunLoop對象
NSRunLoop是基於CFRunLoopRef的一層OC封裝, 所以要瞭解RunLoop內部結構, 需要多研究CFRunLoopRef層面的API(Core Foundation層面)
RunLoop與線程
- 每條線程都有唯一的一個與之對應的RunLoop對象(如果我也想開一個子線成,並且讓線程不死,則子線程開一個RunLoop)
- 主線程的RunLoop已經自動建立好了,子線程的RunLoop需要主動建立
- RunLoop在第一次擷取時建立,線上程結束時銷毀
獲得RunLoop對象
[NSRunLoop currentRunLoop]; // 獲得當前線程的RunLoop對象[NSRunLoop mainRunLoop]; // 獲得主線程的RunLoop對象
CFRunLoopGetCurrent(); // 獲得當前線程的RunLoop對象CFRunLoopGetMain(); // 獲得主線程的RunLoop對象
如果在主線程中: 當前線程的RunLoop對象和主線程的RunLoop對象取得的是相同的。
CFRunLoopSourceRef
- CFRunLoopModeRef代表RunLoop的運行模式
- 一個
RunLoop包含若干個Mode,每個Mode又包含若干個Source/Timer/Observer
- 每次RunLoop啟動時,只能指定其中一個 Mode,這個Mode被稱作 CurrentMode(可以擷取到
[NSRunLoop currentRunLoop].currentMode)
- 如果需要切換Mode,只能退出Loop再重新指定一個Mode進入(因為RunLoop是一個運行迴圈, 一直在跑圈, 換另一個模式,必須先退出, 然後按照另一個模式跑圈)
- 這樣做主要是為了分隔開不同組的
Source/Timer/Observer,讓其互不影響(切換模式是為了,讓它按照另一個模式的Source,Timer,Observer來跑圈, 互不影響)
RunLoop 啟動必須要傳入一個模式,RunLoop有多個模式, 但是每次只能運行一種模式
RunLoop定義兩個Version的Source
- Source0:處理App內部事件,App自己負責管理(觸發),如UIEvent,CFSocket
- Source1:由RunLoop和核心管理,Mach port驅動 如CFMach、CFMessage
CFRunLoopObserverRef
向內部報告RunLoop目前狀態的更改 CAAnimation
可以監聽的時間點如下幾點:
RunLoopObserver 與 Autorelease Pool
UIKit通過RunLoopObserver在RunLoop兩次Sleep間對AutoreleasePool進行pop和push,將這次Loop中產生的Autorelease對象釋放。(好像swift中沒有關於釋放的問題)
CFRunLoopModeRef
RunLoop在同一時段只能且必須在一種特定Mode下Run更換Mode時, 需要暫停當前的Loop,然後重啟新的LoopNSDefalutRunLoopMode? ? ? 預設狀態.空閑狀態UITrackingRunLoopMode? ? ?滑動ScrollViewUIInitializationRunLoopMode? ? 私人,App啟動時NSRunLoopCommonModes? ? ?預設包括上面第一和第二
CFRunLoopTimerRef
- CFRunLoopTimerRef是基於時間的觸發器(基本上說的就是NSTimer), 一個模式下可以有多個Timer(Arrar中存放)
/** * 這個方法內部實現是: 建立timer,添加到RunLoop中的預設的Mode中,RunLoop啟動這個mode,取出這個mode中timer來用 */ [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(run) userInfo:nil repeats:YES]; /** * 上面的代碼等同於下面的 */ // 建立Timer NSTimer *timer = [NSTimer timerWithTimeInterval:0.5 target:self selector:@selector(run) userInfo:nil repeats:YES]; // 定時器只運行在 NSDefaultRunLoopMode 模式下, 一旦RunLoop進入其他模式,這個定時器就不會工作 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; // 如果拖動時, 我們將定時器添打上這個NSRunLoopCommonModes的標記 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; NSLog(@"-----------%@", [NSRunLoop currentRunLoop]); /** * 定時器會跑在標記為common modes的模式下(這個模式只是個標記) * RunLoop會尋找帶有common標籤的模式,有這個標籤的,都可以跑 * 列印當前的RunLoop資訊輸出為:(有common modes標籤的有兩個,UITrackingRunLoopMode和kCFRunLoopDefaultMode),所以定時器可以在這兩個模式下跑, RunLoop只會運行一種模式 common modes = <CFBasicHash 0x7fb8b2700490 [0x10ec6ba40]>{type = mutable set, count = 2, entries => 0 : <CFString 0x10fba2210 [0x10ec6ba40]>{contents = "UITrackingRunLoopMode"} 2 : <CFString 0x10ec8c5e0 [0x10ec6ba40]>{contents = "kCFRunLoopDefaultMode"} } */
AFNetWorking 中建立RunLoop
[[NSThread currentThread] setName:@"AFNetworking"];NSRunLoop *runloop = [NSRunLoop currentRunLoop];[runLoop addPort:[NSMachPort port] forMode:NSDefalutRunLoopMode]//一直活著[runLoop run];
這在處理網路響應是一個很好的方法
UITrackingRunLoopMode 與 NSTimer
// 預設情況下NSTimer被加入NSDefalutRunLoopMode//如果想NSTimer受到組件或者動畫影響 添加到NSRunLoopCommonModes [[NSRunLoop currentRunLoop]addTimer:timer...forMode:NSRunLoopCommonModes];
RunLoop的處理邏輯是什麼呢?
每次運行run loop,你線程的run loop對會自動處理之前未處理的訊息,並通知相關的觀察者。具體的順序如下:
- 通知觀察者run loop已經啟動
- 通知觀察者任何即將要開始的定時器
- 通知觀察者任何即將啟動的非基於連接埠的源
- 啟動任何準備好的非基於連接埠的源
- 如果基於連接埠的源準備好並處於等待狀態,立即啟動;並進入步驟9。
- 通知觀察者線程進入休眠
- 將線程置於休眠直到任一下面的事件發生:
- 某一事件到達基於連接埠的源
- 定時器啟動
- Run loop設定的時間已經逾時
- run loop被顯式喚醒
- 通知觀察者線程將被喚醒。
- 處理未處理的事件
- 如果使用者定義的定時器啟動,處理定時器事件並重啟run loop。進入步驟2
- 如果輸入源啟動,傳遞相應的訊息
- 如果run loop被顯式喚醒而且時間還沒逾時,重啟run loop。進入步驟2
- 通知觀察者run loop結束。
好的文章都是值得反覆看的,我們在不同的階段來相同的文章或資料都能有不同的收穫,提高自己對知識的理解,聲明一下:最好是自己理解後再總結一次,不要一味的收藏,每個程式員都有著成為大牛的潛質,只在是否努力。加油 技術宅們!
iOS開發中對RunLoop的個人心得