『ios』 dispatch_async(dispatch_get_main_queue(), ^{ })與runloop

我們都知道runloop是一個(gè)循環(huán),那么我有這么一個(gè)問(wèn)題,dispatch_get_main_queue()是在當(dāng)次循環(huán)還是下次循環(huán)執(zhí)行仓洼。于是座韵,進(jìn)行了下面這波測(cè)試和分析险绘。

dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"1");
    });

先通過(guò)監(jiān)聽(tīng)runloop的各個(gè)狀態(tài)來(lái)測(cè)試

void myRunLoopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
    switch(activity)
    {
            // 即將進(jìn)入Loop
        case kCFRunLoopEntry:
            NSLog(@"run loop entry");
            break;
        case kCFRunLoopBeforeTimers://即將處理 Timer
            NSLog(@"run loop before timers");
            break;
        case kCFRunLoopBeforeSources://即將處理 Source
            NSLog(@"run loop before sources");
            break;
        case kCFRunLoopBeforeWaiting://即將進(jìn)入休眠
            NSLog(@"run loop before waiting");
            break;
        case kCFRunLoopAfterWaiting://剛從休眠中喚醒
            NSLog(@"run loop after waiting");
            break;
        case kCFRunLoopExit://即將退出Loop
            NSLog(@"run loop exit");
            break;
        default:
            break;
    }
}

image.png

通過(guò)上面分析,感覺(jué)是下次循環(huán)誉碴。
image.png

CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE 這個(gè)東西是什么呢宦棺?

然后又去找了下源碼。

CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION
CFRunloop is calling out to an abserver callback function
用于向外部報(bào)告 RunLoop 當(dāng)前狀態(tài)的更改黔帕,框架中很多機(jī)制都由 RunLoopObserver 觸發(fā)代咸,如 CAAnimation
 
CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK
CFRunloop is calling out to a block
消息通知、非延遲的perform成黄、dispatch調(diào)用呐芥、block回調(diào)逻杖、KVO
 
CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
CFRunloop is servicing the main desipatch queue
 
CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION
CFRunloop is calling out to a timer callback function
延遲的perform, 延遲dispatch調(diào)用
 
CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION
CFRunloop is calling out to a source 0 perform function
處理App內(nèi)部事件、App自己負(fù)責(zé)管理(觸發(fā))思瘟,如UIEvent荸百、CFSocket。普通函數(shù)調(diào)用滨攻,系統(tǒng)調(diào)用
 
CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION
CFRunloop is calling out to a source 1 perform function

看到這里好像還不能知道為什么够话。

{
    // 1.1 通知Observers,即將進(jìn)入RunLoop
    // 此處有Observer會(huì)創(chuàng)建AutoreleasePool: _objc_autoreleasePoolPush();
    __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry);
    do {
 
        // 2.1 通知 Observers: 即將觸發(fā) Timer 回調(diào)光绕。
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers);
        // 2.2 通知 Observers: 即將觸發(fā) Source (非基于port的,Source0) 回調(diào)更鲁。
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources);
         // 執(zhí)行Block
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
 
        // 2.3 觸發(fā) Source0 (非基于port的) 回調(diào)。
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0);
        // 執(zhí)行Block
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
 
        // 3.1 通知Observers奇钞,即將進(jìn)入休眠
        // 此處有Observer釋放并新建AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush();
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeWaiting);
 
        // 3.2 sleep to wait msg.
        mach_msg() -> mach_msg_trap();
 
        // 3.3 通知Observers澡为,線程被喚醒
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopAfterWaiting);
 
        // 4.1 如果是被Timer喚醒的,回調(diào)Timer
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(timer);
 
        // 4.2 如果是被dispatch喚醒的景埃,執(zhí)行所有調(diào)用 dispatch_async 等方法放入main queue 的 block
        __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block);
 
        // 4.3 如果如果Runloop是被 Source1 (基于port的) 的事件喚醒了媒至,處理這個(gè)事件
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1);
 
        // 5. 退出判斷函數(shù)調(diào)用棧無(wú)顯示
    } while (...);
 
    // 6. 通知Observers,即將退出RunLoop
    // 此處有Observer釋放AutoreleasePool: _objc_autoreleasePoolPop();
    __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopExit);
}

看完上面的內(nèi)容谷徙,我覺(jué)得可以這么理解拒啰,通過(guò)dispatch_asyn注冊(cè)到observer中,當(dāng)runloop下次循環(huán)的時(shí)候完慧,執(zhí)行所有通過(guò)dispatch_asyn添加進(jìn)去的事件谋旦。
所以應(yīng)該是下次循環(huán)。
看一下下面的代碼屈尼,理解可能會(huì)更深一點(diǎn)册着。

/**
 *  運(yùn)行run loop
 *
 *  @param rl              運(yùn)行的RunLoop對(duì)象
 *  @param rlm             運(yùn)行的mode
 *  @param seconds         run loop超時(shí)時(shí)間
 *  @param stopAfterHandle true:run loop處理完事件就退出  false:一直運(yùn)行直到超時(shí)或者被手動(dòng)終止
 *  @param previousMode    上一次運(yùn)行的mode
 *
 *  @return 返回4種狀態(tài)
 */
 static int32_t __CFRunLoopRun(CFRunLoopRef rl, 
                               CFRunLoopModeRef rlm,
                               CFTimeInterval seconds, 
                               CFRunLoopModeRef previousMode) {
    // 借助GCD timer,設(shè)置runloop的超時(shí)時(shí)間
    dispatch_source_t timeout_timer = ...
    // 超時(shí)回調(diào)函數(shù) __CFRunLoopTimeout
    dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout); 
    // 超時(shí)取消函數(shù)  __CFRunLoopTimeoutCancel
    dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel); 

    // 進(jìn)入do while循環(huán)脾歧,開(kāi)始run
    int32_t retVal = 0; // runloop run返回值甲捏,默認(rèn)為0,會(huì)在do while中根據(jù)情況被修改鞭执,當(dāng)不為0時(shí)司顿,runloop退出
    do{
        mach_port_t livePort = MACH_PORT_NULL; // 用于記錄喚醒休眠的RunLoop的mach port,休眠前是NULL
        __CFPortSet waitSet = rlm->_portSet; // 取當(dāng)前mode所需要監(jiān)聽(tīng)的mach port集合兄纺,用于喚醒runloop(__CFPortSet 實(shí)際上是unsigned int 類(lèi)型)

        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);  // 通知 即將處理 Timers
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources); // 通知 即將處理 sources

        // 處理提交到runloop的blocks
        __CFRunLoopDoBlocks(rl, rlm);

        // 處理 source0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle); 
        if (sourceHandledThisLoop) {
            __CFRunLoopDoBlocks(rl, rlm); // 處理提交的runloop的block
        }

        // 如果有source1被signaled大溜,則不休眠,直接跳到handle_msg來(lái)處理source1
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                goto handle_msg; 
         }

        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting); // 通知observer before waiting

        // ****** 開(kāi)始休眠 ******
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy); // 調(diào)用__CFRunLoopServiceMachPort, 監(jiān)聽(tīng)waitSet所指定的mach port端口集合, 如果沒(méi)有port message估脆,進(jìn)入 mach_msg_trap, RunLoop休眠,直到收到port message或者超時(shí)

      // ****** 休眠結(jié)束 ******
      __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting); // 通知observer钦奋, runloop醒了

handle_msg:; // 處理事件
     // 根據(jù)喚醒RunLoop的livePort值,來(lái)進(jìn)行對(duì)應(yīng)邏輯處理
     if (MACH_PORT_NULL == livePort) { // MACH_PORT_NULL: 可能是休眠超時(shí),啥都不做
            CFRUNLOOP_WAKEUP_FOR_NOTHING();
            // handle nothing
      } else if (livePort == rl->_wakeUpPort) { // rl->_wakeUpPort: 被其他線程或進(jìn)程喚醒锨苏,啥都不做
            CFRUNLOOP_WAKEUP_FOR_WAKEUP();
            // do nothing on Mac OS
      } else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) // rlm->_timerPort: 處理nstimer 消息
         if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer
                __CFArmNextTimerInMode(rlm, rl);
            }
      }else if (livePort == dispatchPort) // dispatchPort:處理分發(fā)到main queue上的事件
      {
          __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
      }else {   // 其余的疙教,肯定是各種source1 事件
          __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
          if (NULL != reply) { // 如果有需要回復(fù)soruce1的消息棺聊,則回復(fù)
                (void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
                    CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
           }
      }
     // ****** 到這里就結(jié)束了對(duì)livePort的處理 ******

     __CFRunLoopDoBlocks(rl, rlm);  // 處理提交到runloop的blocks

     // 檢查runloop是否需要退出
    if (sourceHandledThisLoop && stopAfterHandle) { // case1. 指定了僅處理一個(gè)source 退出kCFRunLoopRunHandledSource
        retVal = kCFRunLoopRunHandledSource;
    } else if (timeout_context->termTSR < mach_absolute_time()) { // case2. RunLoop 超時(shí)
        retVal = kCFRunLoopRunTimedOut;
    } else if (__CFRunLoopIsStopped(rl)) { // case3. RunLoop 被終止
        __CFRunLoopUnsetStopped(rl);
        retVal = kCFRunLoopRunStopped;
    } else if (rlm->_stopped) {  // case4. RunLoop Mode 被終止
        rlm->_stopped = false;
        retVal = kCFRunLoopRunStopped;
    } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) { // case5. RunLoop Mode里面沒(méi)有任何要被處理的事件了(沒(méi)有source0伞租,source1, timer限佩,以及提交到當(dāng)前runloop mode的block)
        retVal = kCFRunLoopRunFinished;
    }
    }while(0 == retVal);

    // runloop循環(huán)結(jié)束葵诈,返回退出原因
    return retVal;
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市祟同,隨后出現(xiàn)的幾起案子作喘,更是在濱河造成了極大的恐慌,老刑警劉巖晕城,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泞坦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡砖顷,警方通過(guò)查閱死者的電腦和手機(jī)贰锁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)滤蝠,“玉大人豌熄,你說(shuō)我怎么就攤上這事∥锟龋” “怎么了锣险?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)览闰。 經(jīng)常有香客問(wèn)我芯肤,道長(zhǎng),這世上最難降的妖魔是什么压鉴? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任纷妆,我火速辦了婚禮,結(jié)果婚禮上晴弃,老公的妹妹穿的比我還像新娘掩幢。我一直安慰自己,他們只是感情好上鞠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布际邻。 她就那樣靜靜地躺著,像睡著了一般芍阎。 火紅的嫁衣襯著肌膚如雪世曾。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,763評(píng)論 1 307
  • 那天谴咸,我揣著相機(jī)與錄音轮听,去河邊找鬼骗露。 笑死,一個(gè)胖子當(dāng)著我的面吹牛血巍,可吹牛的內(nèi)容都是我干的萧锉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼述寡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼柿隙!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起鲫凶,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤禀崖,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后螟炫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體波附,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年昼钻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掸屡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡换吧,死狀恐怖折晦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沾瓦,我是刑警寧澤满着,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站贯莺,受9級(jí)特大地震影響风喇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缕探,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一魂莫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧爹耗,春花似錦耙考、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至山卦,卻和暖如春鞋邑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工枚碗, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逾一,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓肮雨,卻偏偏與公主長(zhǎng)得像遵堵,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酷含,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

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