iOS runloop 源碼流程驗證探究

iOS runloop 源碼流程驗證探究

我們通過在 touchbegin 加一個斷點,然后點擊屏幕,然后再控制臺輸入 bt肤晓,來看見調(diào)用堆棧


bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x000000010d53acfd blogTest`-[ViewController touchesBegan:withEvent:](self=0x00007fa075707410, _cmd="touchesBegan:withEvent:", touches=1 element, event=0x0000600000fe8a00) at ViewController.m:149:5
    frame #1: 0x0000000111329863 UIKitCore`forwardTouchMethod + 340
    frame #2: 0x00000001113296fe UIKitCore`-[UIResponder touchesBegan:withEvent:] + 49
    frame #3: 0x00000001113388de UIKitCore`-[UIWindow _sendTouchesForEvent:] + 1867
    frame #4: 0x000000011133a4c6 UIKitCore`-[UIWindow sendEvent:] + 4596
    frame #5: 0x000000011131553b UIKitCore`-[UIApplication sendEvent:] + 356
    frame #6: 0x000000011139671a UIKitCore`__dispatchPreprocessedEventFromEventQueue + 6847
    frame #7: 0x00000001113991e0 UIKitCore`__handleEventQueueInternal + 5980
    frame #8: 0x000000010df65471 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #9: 0x000000010df6539c CoreFoundation`__CFRunLoopDoSource0 + 76
    frame #10: 0x000000010df64b74 CoreFoundation`__CFRunLoopDoSources0 + 180
    frame #11: 0x000000010df5f87f CoreFoundation`__CFRunLoopRun + 1263
    frame #12: 0x000000010df5f066 CoreFoundation`CFRunLoopRunSpecific + 438
    frame #13: 0x0000000115928bb0 GraphicsServices`GSEventRunModal + 65
    frame #14: 0x00000001112fcd4d UIKitCore`UIApplicationMain + 1621
    frame #15: 0x000000010d53de94 blogTest`main(argc=1, argv=0x00007ffee26c5d60) at main.m:20:12
    frame #16: 0x000000010f8c1c25 libdyld.dylib`start + 1

可以看到 是從 CFRunLoopRunSpecific 這個位置開始調(diào)用 runloop 的.

CFRunLoopRunSpecific

我們看下源碼



SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
    __CFRunLoopLock(rl);
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
    Boolean did = false;
    if (currentMode) __CFRunLoopModeUnlock(currentMode);
    __CFRunLoopUnlock(rl);
    return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
    }
    volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
    CFRunLoopModeRef previousMode = rl->_currentMode;
    rl->_currentMode = currentMode;
    int32_t result = kCFRunLoopRunFinished;

    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

        __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopPopPerRunData(rl, previousPerRun);
    rl->_currentMode = previousMode;
    __CFRunLoopUnlock(rl);
    return result;
}


我們可以看到這樣一句話啊

// 通知 observer 進入
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);

// 開始 run
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);

// 通知 observer 退出
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

我們接著看下 __CFRunLoopRun 這個函數(shù)

這段源碼特別長,但是我們?yōu)榱丝戳鞒蹋@段代碼里面有這樣一段

// 處理 timer 事件
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);米碰、
// 處理 source 事件
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);

、// 處理block
    __CFRunLoopDoBlocks(rl, rlm);
    
           Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);

// 處理 port购城,也就是source1事件
        if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
            msg = (mach_msg_header_t *)msg_buffer;
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) {
                goto handle_msg;
            }
#elif DEPLOYMENT_TARGET_WINDOWS
            if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) {
                goto handle_msg;
            }
#endif
        }
        
        // 即將休眠
            if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
    __CFRunLoopSetSleeping(rl)
    
    // 等待別的線程喚醒
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
        
        // 結(jié)束休眠
            __CFRunLoopUnsetSleeping(rl);
    if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

可以看到吕座,和我們之前分析的是一樣的,先處理 timer事件瘪板,然后處理 sources 事件吴趴,緊接著處理 block,緊接著判斷是否有 source1 事件,有的話就回去處理侮攀,如果沒有锣枝,就會進入即將休眠事件,然后開始休眠兰英,等待消息喚醒它撇叁,然后結(jié)束休眠,都是通過 __CFRunLoopDoObservers 這個函數(shù)來通知的畦贸。

其中 __CFRunLoopServiceMachPort 是讓當(dāng)前線程休眠陨闹,內(nèi)部調(diào)用了 mach_msg 函數(shù),是個內(nèi)核函數(shù)薄坏,用戶態(tài)向內(nèi)核態(tài)的 轉(zhuǎn)化趋厉,他的作用是等待消息喚醒,沒有消息就讓線程休眠胶坠,有消息了就喚醒線程君账。當(dāng)喚醒的時候又從內(nèi)核態(tài)切換到了用戶態(tài)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涵但,一起剝皮案震驚了整個濱河市杈绸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌矮瘟,老刑警劉巖瞳脓,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異澈侠,居然都是意外死亡劫侧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來烧栋,“玉大人写妥,你說我怎么就攤上這事∩笮眨” “怎么了珍特?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長魔吐。 經(jīng)常有香客問我扎筒,道長,這世上最難降的妖魔是什么酬姆? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任嗜桌,我火速辦了婚禮,結(jié)果婚禮上辞色,老公的妹妹穿的比我還像新娘骨宠。我一直安慰自己,他們只是感情好相满,可當(dāng)我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布层亿。 她就那樣靜靜地躺著,像睡著了一般雳灵。 火紅的嫁衣襯著肌膚如雪棕所。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天悯辙,我揣著相機與錄音琳省,去河邊找鬼。 笑死躲撰,一個胖子當(dāng)著我的面吹牛针贬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拢蛋,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼桦他,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了谆棱?” 一聲冷哼從身側(cè)響起快压,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎垃瞧,沒想到半個月后蔫劣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡个从,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年脉幢,在試婚紗的時候發(fā)現(xiàn)自己被綠了歪沃。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡嫌松,死狀恐怖沪曙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情萎羔,我是刑警寧澤液走,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站贾陷,受9級特大地震影響育灸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜昵宇,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望儿子。 院中可真熱鬧瓦哎,春花似錦、人聲如沸柔逼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽愉适。三九已至犯助,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間维咸,已是汗流浹背剂买。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留癌蓖,地道東北人瞬哼。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像租副,于是被迫代替她去往敵國和親坐慰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,652評論 2 354