iOS開發(fā)基礎(chǔ)之Runloop

文章內(nèi)容來自:
深入理解RunLoop
官方文檔-Runloop

Runloop概念

  • Runloop本身是一個(gè)對(duì)象(CFRunloopRef或是NSRunloop )
  • 管理事件和消息
  • 提供一種機(jī)制:接收到消息或是事件處于運(yùn)行狀態(tài)徙缴,否則處于休眠避免CPU資源的浪費(fèi)

Runloop和線程的關(guān)系

  • 線程和Runloop是一一對(duì)應(yīng)的双戳,系統(tǒng)持有一個(gè)全局的CFMutableDictionary來保存線程和Runloop盅视;其中key是線程压汪,value對(duì)應(yīng)CFRunloopRef實(shí)例缠导;

  • 線程的最初創(chuàng)建如果不獲取Runloop是不會(huì)自動(dòng)創(chuàng)建的

Runloop組成

Runloop組成

一個(gè)runloop中存在多個(gè)Mode辩块,每個(gè)Mode中存在Source/Observer/Timer 靴寂;每次調(diào)用 RunLoop 的主函數(shù)時(shí),只能指定其中一個(gè) Mode轴捎,這個(gè)Mode被稱作 CurrentMode鹤盒。如果需要切換 Mode,只能退出runloop侦副,再重新指定一個(gè) Mode 進(jìn)入侦锯。這樣做主要是為了分隔開不同組的 Source/Timer/Observer,讓其互不影響

CFRunloopSourceRef (事件產(chǎn)生)
  • source0:包含一個(gè)回調(diào)的函數(shù)指針秦驯,它不能主動(dòng)觸發(fā)事件尺碰,需要調(diào)用函數(shù)將其標(biāo)記,然后再喚醒runloop執(zhí)行译隘;
  • source1:包含一個(gè)mach_port和函數(shù)指針亲桥,被用于處理內(nèi)核和其他線程發(fā)送過來的消息,可以直接喚醒runloop固耘;

CFRunloopTimerRef

CFRunloopTimerRef包含一個(gè)時(shí)間長(zhǎng)度和函數(shù)指針两曼,runloop會(huì)注冊(cè)時(shí)間,當(dāng)時(shí)間一到就會(huì)觸發(fā)回調(diào)函數(shù)玻驻;

CFRunloopObserverRef

CFRunloopObserverRef包含一個(gè)函數(shù)指針,它可以監(jiān)控runloop的狀態(tài)偿枕,一旦狀態(tài)改變就會(huì)觸發(fā)回調(diào)函數(shù)璧瞬;

 typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry         = (1UL << 0), // 即將進(jìn)入Loop
kCFRunLoopBeforeTimers  = (1UL << 1), // 即將處理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即將處理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即將進(jìn)入休眠
kCFRunLoopAfterWaiting  = (1UL << 6), // 剛從休眠中喚醒
kCFRunLoopExit          = (1UL << 7), // 即將退出Loop
};  

Source/Timer/Observer 被統(tǒng)稱為 mode item,一個(gè) item 可以被同時(shí)加入多個(gè) mode渐夸。但一個(gè) item 被重復(fù)加入同一個(gè) mode 時(shí)是不會(huì)有效果的嗤锉。如果一個(gè) mode 中一個(gè) item 都沒有,則 RunLoop 會(huì)直接退出墓塌,不進(jìn)入循環(huán)

Runloop的Mode

CFRunLoopMode 和 CFRunLoop 的結(jié)構(gòu)大致如下:

struct __CFRunLoopMode {
CFStringRef _name;            // Mode Name, 例如 @"kCFRunLoopDefaultMode"
CFMutableSetRef _sources0;    // Set
CFMutableSetRef _sources1;    // Set
CFMutableArrayRef _observers; // Array
CFMutableArrayRef _timers;    // Array
...
};

struct __CFRunLoop {
CFMutableSetRef _commonModes;     // Set
CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>
CFRunLoopModeRef _currentMode;    // Current Runloop Mode
CFMutableSetRef _modes;           // Set
...
};   

RunLoop 的內(nèi)部邏輯

RunLoop 的內(nèi)部邏輯

當(dāng)以一種Mode啟動(dòng)runloop時(shí)瘟忱,CFRunLoopRunSpecific函數(shù)會(huì)根據(jù)ModeName找到對(duì)應(yīng)的Mode,判斷Mode內(nèi)部是否存在source/observer/timer,如果存在會(huì)調(diào)用CFRunloopRun函數(shù)苫幢,CFRunloopRun內(nèi)部存在一個(gè)while循環(huán)访诱,runloop會(huì)通知observers將要分別啟動(dòng)timer、source0韩肝、source1触菜,一旦啟動(dòng)observers中對(duì)應(yīng)的回調(diào)函數(shù)將會(huì)被調(diào)用;runloop會(huì)通過mach_msg()函數(shù)接收系統(tǒng)消息哀峻,當(dāng)這一切執(zhí)行完畢涡相,當(dāng)前runloop就會(huì)退出哲泊!

AutoreleasePool

一個(gè)runloop循環(huán)可以理解創(chuàng)建一個(gè)AutoreleasePool,在啟動(dòng)和離開時(shí)分別觸發(fā)AutoreleasePool的push和pop函數(shù)催蝗,在此次循環(huán)結(jié)束后完成內(nèi)存的清理工作切威;

事件響應(yīng)

  • 蘋果注冊(cè)了一個(gè) Source1 (基于 mach port 的) 用來接收系統(tǒng)事件,其回調(diào)函數(shù)為 __IOHIDEventSystemClientQueueCallback()
  • 觸摸丙号,加速先朦,接近傳感器等幾種 Event,隨后用 mach port 轉(zhuǎn)發(fā)給需要的App進(jìn)程槽袄。隨后蘋果注冊(cè)的那個(gè) Source1 就會(huì)觸發(fā)回調(diào)烙无,并調(diào)用 _UIApplicationHandleEventQueue() 進(jìn)行應(yīng)用內(nèi)部的分發(fā)。
  • _UIApplicationHandleEventQueue() 會(huì)把 IOHIDEvent 處理并包裝成 UIEvent 進(jìn)行處理或分發(fā)遍尺,其中包括識(shí)別 UIGesture/處理屏幕旋轉(zhuǎn)/發(fā)送給 UIWindow 等截酷。通常事件比如 UIButton 點(diǎn)擊、touchesBegin/Move/End/Cancel 事件都是在這個(gè)回調(diào)中完成的乾戏。

事件的分發(fā)有一個(gè)hit-testing的過程迂苛,這樣能夠找到最終處理時(shí)間的控件,進(jìn)而執(zhí)行自定義的邏輯鼓择!

手勢(shì)識(shí)別

  • 當(dāng)上面的 _UIApplicationHandleEventQueue() 識(shí)別了一個(gè)手勢(shì)時(shí)三幻,其首先會(huì)調(diào)用 Cancel 將當(dāng)前的 touchesBegin/Move/End 系列回調(diào)打斷。隨后系統(tǒng)將對(duì)應(yīng)的 UIGestureRecognizer 標(biāo)記為待處理呐能。
  • 蘋果注冊(cè)了一個(gè) Observer 監(jiān)測(cè) BeforeWaiting (Loop即將進(jìn)入休眠) 事件念搬,這個(gè)Observer的回調(diào)函數(shù)是 _UIGestureRecognizerUpdateObserver(),其內(nèi)部會(huì)獲取所有剛被標(biāo)記為待處理的 GestureRecognizer摆出,并執(zhí)行GestureRecognizer的回調(diào)
  • 當(dāng)有 UIGestureRecognizer 的變化(創(chuàng)建/銷毀/狀態(tài)改變)時(shí)朗徊,這個(gè)回調(diào)都會(huì)進(jìn)行相應(yīng)處理。

界面更新

  • 當(dāng)在操作 UI 時(shí)偎漫,比如改變了 Frame爷恳、更新了 UIView/CALayer 的層次時(shí),或者手動(dòng)調(diào)用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后象踊,這個(gè) UIView/CALayer 就被標(biāo)記為待處理温亲,并被提交到一個(gè)全局的容器去。
  • 蘋果注冊(cè)了一個(gè) Observer 監(jiān)聽 BeforeWaiting(即將進(jìn)入休眠) 和 Exit (即將退出Loop) 事件杯矩,回調(diào)去執(zhí)行一個(gè)很長(zhǎng)的函數(shù):
    _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()栈虚。這個(gè)函數(shù)里會(huì)遍歷所有待處理的 UIView/CAlayer 以執(zhí)行實(shí)際的繪制和調(diào)整,并更新 UI 界面

關(guān)于網(wǎng)絡(luò)請(qǐng)求

  • CFSocket 是最底層的接口史隆,只負(fù)責(zé) socket 通信节芥。
  • CFNetwork 是基于 CFSocket 等接口的上層封裝,ASIHttpRequest 工作于這一層。
  • NSURLConnection 是基于 CFNetwork 的更高層的封裝头镊,提供面向?qū)ο蟮慕涌隍纪眨珹FNetworking 工作于這一層。
  • NSURLSession 是 iOS7 中新增的接口相艇,表面上是和 NSURLConnection 并列的颖杏,但底層仍然用到了 NSURLConnection 的部分功能 (比如 com.apple.NSURLConnectionLoader 線程),AFNetworking2 和 Alamofire 工作于這一層坛芽。

與 Runloop 相關(guān)的實(shí)例

NSTimer注冊(cè)的事件不被執(zhí)行

日常開發(fā)中留储,與 runLoop 接觸得最近可能就是通過 NSTimer 了。一個(gè) Timer 一次只能加入到一個(gè) RunLoop 中咙轩。我們?nèi)粘J褂玫臅r(shí)候获讳,通常就是加入到當(dāng)前的 runLoop 的 default mode 中,而 ScrollView 在用戶滑動(dòng)時(shí)活喊,主線程 RunLoop 會(huì)轉(zhuǎn)到 UITrackingRunLoopMode 丐膝。而這個(gè)時(shí)候, Timer 就不會(huì)運(yùn)行钾菊。


有如下兩種解決方案:
第一種: 設(shè)置RunLoop Mode帅矗,例如NSTimer,我們指定它運(yùn)行于 NSRunLoopCommonModes ,這是一個(gè)Mode的集合煞烫。注冊(cè)到這個(gè) Mode 下后浑此,無論當(dāng)前 runLoop 運(yùn)行哪個(gè) mode ,事件都能得到執(zhí)行滞详。
第二種: 另一種解決Timer的方法是凛俱,我們?cè)诹硗庖粋€(gè)線程執(zhí)行和處理 Timer 事件,然后在主線程更新UI料饥。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末最冰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子稀火,更是在濱河造成了極大的恐慌,老刑警劉巖赌朋,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凰狞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡沛慢,警方通過查閱死者的電腦和手機(jī)赡若,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來团甲,“玉大人逾冬,你說我怎么就攤上這事。” “怎么了身腻?”我有些...
    開封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵产还,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我嘀趟,道長(zhǎng)脐区,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任她按,我火速辦了婚禮牛隅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘酌泰。我一直安慰自己媒佣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開白布陵刹。 她就那樣靜靜地躺著默伍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪授霸。 梳的紋絲不亂的頭發(fā)上巡验,一...
    開封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音碘耳,去河邊找鬼显设。 笑死,一個(gè)胖子當(dāng)著我的面吹牛辛辨,可吹牛的內(nèi)容都是我干的捕捂。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼斗搞,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼指攒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起僻焚,我...
    開封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤允悦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后虑啤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體隙弛,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年狞山,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了全闷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡萍启,死狀恐怖总珠,靈堂內(nèi)的尸體忽然破棺而出屏鳍,到底是詐尸還是另有隱情,我是刑警寧澤局服,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布钓瞭,位于F島的核電站,受9級(jí)特大地震影響腌逢,放射性物質(zhì)發(fā)生泄漏降淮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一搏讶、第九天 我趴在偏房一處隱蔽的房頂上張望佳鳖。 院中可真熱鬧,春花似錦媒惕、人聲如沸系吩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽穿挨。三九已至,卻和暖如春肴盏,著一層夾襖步出監(jiān)牢的瞬間科盛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工菜皂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贞绵,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓恍飘,卻偏偏與公主長(zhǎng)得像榨崩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子章母,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容