iOS底層探索28、Run Loop

  • 關(guān)于 Run Loop 的文章在之前的博客已做過(guò)分析,這里將文章轉(zhuǎn)移到簡(jiǎn)書,并進(jìn)行一些信息補(bǔ)充。

RunLoop 源碼地址

RunLoop 官方文檔 Threading Programming Guide

image

何時(shí)要用 run loop ?

  • 應(yīng)用程序主線程的 run loop 是基礎(chǔ)結(jié)構(gòu)的關(guān)鍵部分。因此,應(yīng)用程序框架提供了運(yùn)行主應(yīng)用程序循環(huán)的代碼,并自動(dòng)啟動(dòng)該循環(huán)。
  • 對(duì)于輔助線程,您需要決定是否需要運(yùn)行循環(huán),如果需要,自己配置并啟動(dòng)它。
  • 不需要在所有情況下啟動(dòng)線程的運(yùn)行循環(huán)。例如,如果您使用一個(gè)線程來(lái)執(zhí)行一些長(zhǎng)時(shí)間運(yùn)行和預(yù)定的任務(wù),您可能可以避免啟動(dòng)運(yùn)行循環(huán)。
  • 運(yùn)行循環(huán)適用于需要與線程進(jìn)行更多交互性的情況。例如,如果你計(jì)劃做以下任何一件事,你需要啟動(dòng)一個(gè)運(yùn)行循環(huán):
    1.使用端口或自定義輸入源與其他線程通信。- 例:線程間 NSPort 通訊 必須加入 runloop 才可正常通訊
    2.在線程上使用計(jì)時(shí)器。
    3.在Cocoa應(yīng)用程序中使用任何performSelector方法。
    4.保持線程運(yùn)行以執(zhí)行周期性任務(wù)。

一、Runloop 介紹

1、RunLoop 是什么?

運(yùn)行循環(huán)是與線程相關(guān)聯(lián)的基礎(chǔ)設(shè)施的一部分。運(yùn)行循環(huán)是一個(gè)事件處理循環(huán),用于調(diào)度工作和協(xié)調(diào)接收傳入事件。

運(yùn)行循環(huán)的目的是讓線程在有工作要做時(shí)保持忙碌,而在沒(méi)有工作要做時(shí)讓線程休眠
運(yùn)行循環(huán)管理不是完全自動(dòng)的。您仍然必須設(shè)計(jì)線程的代碼,以在適當(dāng)?shù)臅r(shí)間啟動(dòng)運(yùn)行循環(huán)并響應(yīng)傳入的事件。Cocoa 和 Core Foundation 都提供了run loop對(duì)象來(lái)幫助你配置和管理線程的 run loop。您的應(yīng)用程序不需要顯式地創(chuàng)建這些對(duì)象;

每個(gè)線程,包括應(yīng)用程序的主線程,都有一個(gè)關(guān)聯(lián)的run loop對(duì)象。然而,只有輔助線程需要顯式地運(yùn)行它們的run循環(huán)。作為應(yīng)用程序啟動(dòng)過(guò)程的一部分,應(yīng)用程序框架會(huì)在主線程(main)上自動(dòng)設(shè)置并運(yùn)行run循環(huán)。

簡(jiǎn)單來(lái)說(shuō)即:runloop 是一個(gè)對(duì)象,它提供了一個(gè)入口函數(shù),內(nèi)部是一個(gè) do...while... 循環(huán)(并非通常意義上do...while...),循環(huán)內(nèi) 進(jìn)行事件處理。

image

2、RunLoop 作用

runloop 的結(jié)構(gòu)和 source:

image

保持程序不死。--> main 函數(shù)

處理 APP 中的各類事件:觸摸交互、定時(shí)器、performSelect、端口交互... ...

節(jié)省 CPU 資源 --> 有工作時(shí)喚醒,完成/無(wú)任務(wù)時(shí)休眠

3、RunLoop 應(yīng)用

block / timer / 響應(yīng) source0/source1 / GCD 主隊(duì)列 / observe 源

// GCD
static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__() __attribute__((noinline)); static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(void *msg) {
    _dispatch_main_queue_callback_4CF(msg);
    asm __volatile__(""); // thwart tail-call optimization
} // observer
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__() __attribute__((noinline)); static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { if (func) {
        func(observer, activity, info);
    }
    asm __volatile__(""); // thwart tail-call optimization
} // timer
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__() __attribute__((noinline)); static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(CFRunLoopTimerCallBack func, CFRunLoopTimerRef timer, void *info) { if (func) {
        func(timer, info);
    }
    asm __volatile__(""); // thwart tail-call optimization
} // block
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__() __attribute__((noinline)); static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(void (^block)(void)) { if (block) {
        block();
    }
    asm __volatile__(""); // thwart tail-call optimization
} // source0
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__() __attribute__((noinline)); static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info) { if (perform) {
        perform(info);
    }
    asm __volatile__(""); // thwart tail-call optimization
} // source1
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__() __attribute__((noinline)); static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__( #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
                                                                       void *(*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info),
                                                                       mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply, #else
                                                                       void (*perform)(void *), #endif
                                                                       void *info) { if (perform) { #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        *reply = perform(msg, size, kCFAllocatorSystemDefault, info); #else perform(info); #endif }
    asm __volatile__(""); // thwart tail-call optimization
}

4、RunLoop 和線程的關(guān)系

4.1)線程和 runloop 一對(duì)一(key - value)關(guān)系

runloop 源碼中可得出 runloop 和 thread 的 dictionary 的key - value關(guān)系;

--> 從簡(jiǎn)介“每個(gè)線程,包括應(yīng)用程序的主線程,都有一個(gè)關(guān)聯(lián)的run loop對(duì)象”也可得知。

// should only be called by Foundation // t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) { if (pthread_equal(t, kNilPthreadT)) {
        t = pthread_main_thread_np();
    }
    __CFLock(&loopsLock); if (!__CFRunLoops) {
        __CFUnlock(&loopsLock);

        CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); // 創(chuàng)建 runloop: __CFRunLoopCreate()
        CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np()); // 存儲(chǔ) runloop: CFDictionarySetValue()
 CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop); if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
            CFRelease(dict);
        }
        CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
  // 獲取 runloop: CFDictionaryGetValue() 
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock); if (!loop) {
        CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
        loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); if (!loop) {
            CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
            loop = newLoop;
        } // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
        __CFUnlock(&loopsLock);
        CFRelease(newLoop);
    } if (pthread_equal(t, pthread_self())) {
        _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL); if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
            _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
        }
    } return loop;
}

4.2)示例

NSThread *thread = [[NSThread alloc] initWithBlock:^{ // 任務(wù)
      NSLog(@"%@---%@",[NSThread currentThread],[[NSThread currentThread] name]); // 定時(shí)器任務(wù)
      [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
          NSLog(@"子線程 timer -- hello word");
      }];
}];

thread.name = @"my_thread";
[thread start]; </pre>

執(zhí)行結(jié)果:

image

并沒(méi)有執(zhí)行 timer,為何???

子線程中 runloop 并未開啟,默認(rèn)是關(guān)閉不開啟的

我們?cè)?thread block 中加入代碼:[[NSRunLoop currentRunLoop] run];

再次執(zhí)行:timer 執(zhí)行了

image

如何停止 runloop 呢?

1. 設(shè)置 timeout;

2.直接結(jié)束對(duì)應(yīng)的線程

通過(guò)某響應(yīng)事件(觸摸/按鈕等)觸發(fā),代碼如下:

NSThread *thread = [[NSThread alloc] initWithBlock:^{ // 任務(wù)
    NSLog(@"%@---%@",[NSThread currentThread],[[NSThread currentThread] name]); // 定時(shí)器任務(wù)
    [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"子線程 timer -- hello word"); // 事件響應(yīng) isStoping=YES --> 退出線程 --> runloop 停止
        if (self.isStopping) {
            [NSThread exit];
        }
    }];
    [[NSRunLoop currentRunLoop] run];
}];

另:關(guān)于 NSTimer 定時(shí)器,是基于 RunLoop 實(shí)現(xiàn)的(下面會(huì)詳細(xì)分析)

當(dāng)我們啟用 NSTimer 時(shí),并不是按照時(shí)間間隔進(jìn)行循環(huán)調(diào)用的。在定時(shí)器注冊(cè)到 runloop 中后,runloop 會(huì)設(shè)置一個(gè)個(gè)的時(shí)間點(diǎn)進(jìn)行調(diào)用,比如10、20、30...。如果錯(cuò)過(guò)了某個(gè)時(shí)間點(diǎn),定時(shí)器是不會(huì)延時(shí)調(diào)用的,他會(huì)直接等待下一個(gè)時(shí)間點(diǎn)調(diào)用,so 定時(shí)器并不是精準(zhǔn)的。

4.2.1)子線程中使用某些延時(shí)函數(shù)和選擇器時(shí),也必須手動(dòng)開啟 runloop,如下方法

/****************     Delayed perform     ******************/

@interface NSObject (NSDelayedPerforming) - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes; - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay; + (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument; + (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget; @end

@interface NSRunLoop (NSOrderedPerform) - (void)performSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg order:(NSUInteger)order modes:(NSArray<NSRunLoopMode> *)modes; - (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg; - (void)cancelPerformSelectorsWithTarget:(id)target; @end

二、RunLoop 的5個(gè)類

A run loop object provides the main interface for adding input sources, timers, and run-loop observers to your run loop and then running it. Every thread has a single run loop object associated with it. In Cocoa, this object is an instance of the NSRunLoop class. In a low-level application, it is a pointer to a CFRunLoopRef opaque type.

-- runloop 對(duì)象提供了一個(gè)主接口(入口),用于向運(yùn)行循環(huán)添加輸入源、計(jì)時(shí)器和運(yùn)行循環(huán)觀察器,然后運(yùn)行它。每個(gè)線程都有一個(gè)與之關(guān)聯(lián)的運(yùn)行循環(huán)對(duì)象。在Cocoa中,這個(gè)對(duì)象是NSRunLoop 類的一個(gè)實(shí)例。在低級(jí)應(yīng)用程序中,它是一個(gè)指向 CFRunLoopRef 不透明類型的指針。

RunLoop 的5個(gè)類:

image

RunLoop 關(guān)系圖:

image

1、CFRunLoopRef

<-- typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef;

struct **__CFRunLoop** {
    CFRuntimeBase _base;
    pthread_mutex_t _lock; /* locked for accessing mode list */ __CFPort _wakeUpPort; // used for CFRunLoopWakeUp
 Boolean _unused; volatile _per_run_data *_perRunData;              // reset for runs of the run loop
 pthread_t _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes; struct _block_item *_blocks_head; struct _block_item *_blocks_tail;
    CFTypeRef _counterpart;
};

2、CFRunLoopModeRef <-- __CFRunLoopMode 結(jié)構(gòu)體

typedef struct __CFRunLoopMode *CFRunLoopModeRef;

struct **__CFRunLoopMode** {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;    /* must have the run loop locked before locking this */ CFStringRef _name;     // mode name 例如:KCFRunLoopDefaultMode
    Boolean _stopped;
    char _padding[3];
    **CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;**
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet;
    CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS dispatch_source_t _timerSource;
    dispatch_queue_t _queue;
    Boolean _timerFired; // set to true by the source when a timer has fired
 Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO mach_port_t _timerPort;
    Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS DWORD _msgQMask;
    void (*_msgPump)(void);
#endif uint64_t _timerSoftDeadline; /* TSR */ uint64_t _timerHardDeadline; /* TSR */ };

RunLoop 的 mode

A run loop mode is a collection of input sources and timers to be monitored and a collection of run loop observers to be notified. Each time you run your run loop, you specify (either explicitly or implicitly) a particular “mode” in which to run. During that pass of the run loop, only sources associated with that mode are monitored and allowed to deliver their events. (Similarly, only observers associated with that mode are notified of the run loop’s progress.) Sources associated with other modes hold on to any new events until subsequent passes through the loop in the appropriate mode.

==》 runloop mode 是要監(jiān)視的輸入源和計(jì)時(shí)器的集合,以及要通知的運(yùn)行循環(huán)觀察者的集合。每次運(yùn)行 runloop 時(shí),都(顯式或隱式)指定要在其中運(yùn)行的特定“mode”。在 runloop 的傳遞過(guò)程中,只監(jiān)視與該模式關(guān)聯(lián)的源,并允許交付它們的事件。(類似地,只有與該模式關(guān)聯(lián)的觀察者才會(huì)被告知運(yùn)行循環(huán)的進(jìn)度。)與其他模式相關(guān)聯(lián)的源 會(huì)保留任何新事件,直到后續(xù)事件以適當(dāng)?shù)哪J酵ㄟ^(guò)循環(huán)。

mode 類型:

image

runloop mode 關(guān)系圖:

image

一個(gè) RunLoop 包含若干個(gè) Mode,每個(gè) Mode 又包含若干個(gè) Source/Timer/Observer。每次調(diào)用 RunLoop 的主函數(shù)時(shí),只能指定其中一個(gè) Mode,這個(gè)Mode被稱作 CurrentMode。如果需要切換 Mode,只能退出 Loop,再重新指定一個(gè) Mode 進(jìn)入。這樣做主要是為了分隔開不同組的 Source/Timer/Observer,讓其互不影響。

mode item(CFRunLoopTimerRef / CFRunLoopSourceRef / CFRunLoopObserverRef)

2.1)CFRunLoopTimerRef <-- __CFRunLoopTimer

基于時(shí)間的觸發(fā)器,當(dāng)其加入到 RunLoop 時(shí),RunLoop 會(huì)注冊(cè)對(duì)應(yīng)的時(shí)間點(diǎn),當(dāng)時(shí)間點(diǎn)到時(shí),RunLoop會(huì)被喚醒以執(zhí)行那個(gè)回調(diào)。

struct __CFRunLoopTimer {
    CFRuntimeBase _base;
    uint16_t _bits;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFMutableSetRef _rlModes;
    CFAbsoluteTime _nextFireDate;
    CFTimeInterval _interval; /* immutable */ CFTimeInterval _tolerance; /* mutable */ uint64_t _fireTSR; /* TSR units */ CFIndex _order; /* immutable */ CFRunLoopTimerCallBack _callout; /* immutable */ CFRunLoopTimerContext _context; /* immutable, except invalidation */ };
image

以定時(shí)器(其底部是一個(gè) CFRunLoopTimerRef)為例,通過(guò)源碼探索 item mode 關(guān)系流程。

創(chuàng)建個(gè) timer 并將其添加到 runloop 的 mode 中:

- (void)timerTest {
    NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"timer in home -- %@",[[NSRunLoop currentRunLoop] currentMode]);
    }];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

流程:

創(chuàng)建定時(shí)器 --> timer 加入到 runloop 中的 mode 中(timer 加到 items 中) --> 即:CFSetAddValue(rl->_commonModeItems, rlt) --> runloop run --> 函數(shù) CFRunLoopRun() 執(zhí)行 --> CFRunLoopRunSpecific() 中 __CFRunLoopRun --> __CFRunLoopDoBlocks(rl, rlm) --> while 循環(huán)所有items --> mode 判斷(doit CFEqual/CFSetContainsValue) --> 執(zhí)行 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block) --> block() --> function

(item 依賴于 mode mode 依賴于 runloop)

CFRunLoop 歷程部分源碼(runloop完整源碼下載見(jiàn)文章頂部)

1. CFRunLoopAddTimer

void **CFRunLoopAddTimer**(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
    CHECK_FOR_FORK(); if (__CFRunLoopIsDeallocating(rl)) return; if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
    __CFRunLoopLock(rl); if (modeName == kCFRunLoopCommonModes) {
        CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; if (NULL == rl->_commonModeItems) {
            rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
        } // 將 timer 加入 commonModeItems 里面
        **CFSetAddValue(rl->_commonModeItems, rlt);** if (NULL != set) {// 循環(huán),一直加,知道set為null
            CFTypeRef context[2] = {rl, rlt}; /* add new item to all common-modes */
            // __CFRunLoopAddItemToCommonModes  執(zhí)行 CFRunLoopAddTimer
            CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
            CFRelease(set);
        }
    } else {
        ... ...
    }
    __CFRunLoopUnlock(rl);
}

2. CFRunLoopRunSpecific()

result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);

3. __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode)

__CFRunLoopDoBlocks(rl, rlm);

4. doBlock

static Boolean **__CFRunLoopDoBlocks**(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // Call with rl and rlm locked
    if (!rl->_blocks_head) return false; if (!rlm || !rlm->_name) return false;
    Boolean did = false; struct _block_item *head = rl->_blocks_head; struct _block_item *tail = rl->_blocks_tail;
    rl->_blocks_head = NULL;
    rl->_blocks_tail = NULL;
    CFSetRef commonModes = rl->_commonModes;
    CFStringRef curMode = rlm->_name;
    __CFRunLoopModeUnlock(rlm);
    __CFRunLoopUnlock(rl); struct _block_item *prev = NULL; struct _block_item *item = head; // 循環(huán)所有 item
    while (item) { struct _block_item *curr = item;
        item = item->_next;
        Boolean doit = false; if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) { // CFEqual(curr->_mode, curMode): 當(dāng)前的_mode 和 傳過(guò)來(lái)的curMode 是否相同 // 當(dāng)前_mode是kCFRunLoopCommonModes && 傳來(lái)的curMode是rlmodes里的一員 // doit
            doit = **CFEqual**(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && **CFSetContainsValue**(commonModes, curMode));
        } else {
            doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
        } if (!doit) prev = curr; if (doit) { if (prev) prev->_next = item; if (curr == head) head = item; if (curr == tail) tail = prev; void (^block)(void) = curr->_block;
            CFRelease(curr->_mode);
            free(curr); if (doit) { /* block 回調(diào)
                **static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(void (^block)(void)) {
                if (block) {
                block();
                }
                asm __volatile__(""); // thwart tail-call optimization
                }** */ **__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block)**;
                did = true;
            }
            Block_release(block); // do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc
 }
    }

    __CFRunLoopLock(rl);
    __CFRunLoopModeLock(rlm); if (head) {
        tail->_next = rl->_blocks_head;
        rl->_blocks_head = head; if (!rl->_blocks_tail) rl->_blocks_tail = tail;
    } return did;
}

tip:通過(guò)上述流程,我們亦可得出定時(shí)器不準(zhǔn)的具體原因:

定時(shí)器所屬 mode 是 kCFRunLoopDefaultMode,當(dāng)頁(yè)面進(jìn)行滑動(dòng)or其他操作時(shí),mode 是 UITrackingRunLoopMode, mode 來(lái)回切換 --> 在“流程4”中:當(dāng)頁(yè)面滑動(dòng)時(shí) mode 不同,doit 為false 后續(xù) block 不執(zhí)行,回調(diào)便不走了 --> 定時(shí)器停了,直到下次 loop 點(diǎn)繼續(xù)。

2.2)CFRunLoopSourceRef <-- __CFRunLoopSource

事件產(chǎn)生的地方,包含 source0 source1。

struct __CFRunLoopSource {
    CFRuntimeBase _base;
    uint32_t _bits;
    pthread_mutex_t _lock;
    CFIndex _order;            /* immutable */ CFMutableBagRef _runLoops;
    union {
        CFRunLoopSourceContext version0;    /* source0 immutable, except invalidation */ CFRunLoopSourceContext1 version1;    /* source1 immutable, except invalidation */ } _context;
};

image

2.3)CFRunLoopObserverRef <-- __CFRunLoopObserver

觀察者,每個(gè) Observer 都包含了一個(gè)回調(diào)(函數(shù)指針),當(dāng) RunLoop 的狀態(tài)發(fā)生變化時(shí),觀察者就能通過(guò)回調(diào)接受到這個(gè)變化。

struct __CFRunLoopObserver {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFIndex _rlCount;
    CFOptionFlags _activities; /* immutable */ CFIndex _order; /* immutable */ CFRunLoopObserverCallBack _callout; /* immutable */ CFRunLoopObserverContext _context; /* immutable, except invalidation */ };
image

可觀測(cè)的 時(shí)間點(diǎn) 們:

/* Run Loop Observer Activities */ 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
    kCFRunLoopAllActivities = 0x0FFFFFFFU };

一個(gè) item 可以被同時(shí)加入多個(gè) mode。但一個(gè) item 被重復(fù)加入同一個(gè) mode 時(shí)是不會(huì)有效果的。

如果一個(gè) mode 中一個(gè) item 都沒(méi)有,則 RunLoop 會(huì)直接退出,不進(jìn)入循環(huán)。

000)示例 - 簡(jiǎn)單實(shí)現(xiàn) timer 和 事件監(jiān)聽:

主要代碼:

#pragma mark - timer -
- (void)cfTimerDemo {

    CFRunLoopTimerContext context = { 0,
        ((__bridge void *)self),
        NULL,
        NULL,
        NULL
    };
    CFRunLoopRef rlp = CFRunLoopGetCurrent(); /**
     參數(shù)一:用于分配對(duì)象的內(nèi)存
     參數(shù)二:在什么是觸發(fā) (距離現(xiàn)在)
     參數(shù)三:每隔多少時(shí)間觸發(fā)一次
     參數(shù)四:未來(lái)參數(shù)
     參數(shù)五:CFRunLoopObserver的優(yōu)先級(jí) 當(dāng)在Runloop同一運(yùn)行階段中有多個(gè)CFRunLoopObserver 正常情況下使用0
     參數(shù)六:回調(diào),比如觸發(fā)事件,我就會(huì)來(lái)到這里
     參數(shù)七:上下文記錄信息 */ CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, 0, 1, 0, 0, myRunLoopTimerCallBack, &context);
    CFRunLoopAddTimer(rlp, timerRef, kCFRunLoopDefaultMode);
} void myRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info){
    NSLog(@"%@---%@",timer,info);
} #pragma mark - observer -
- (void)cfObseverDemo {

    CFRunLoopObserverContext context = { 0,
        ((__bridge void *)self),
        NULL,
        NULL,
        NULL
    };
    CFRunLoopRef rlp = CFRunLoopGetCurrent(); /**
     參數(shù)一:用于分配對(duì)象的內(nèi)存
     參數(shù)二:你關(guān)注的事件
          kCFRunLoopEntry=(1<<0),
          kCFRunLoopBeforeTimers=(1<<1),
          kCFRunLoopBeforeSources=(1<<2),
          kCFRunLoopBeforeWaiting=(1<<5),
          kCFRunLoopAfterWaiting=(1<<6),
          kCFRunLoopExit=(1<<7),
          kCFRunLoopAllActivities=0x0FFFFFFFU
     參數(shù)三:CFRunLoopObserver是否循環(huán)調(diào)用
     參數(shù)四:CFRunLoopObserver的優(yōu)先級(jí) 當(dāng)在Runloop同一運(yùn)行階段中有多個(gè)CFRunLoopObserver 正常情況下使用0
     參數(shù)五:回調(diào),比如觸發(fā)事件,我就會(huì)來(lái)到這里
     參數(shù)六:上下文記錄信息 */ CFRunLoopObserverRef observerRef = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, myRunLoopObserverCallBack, &context);
    CFRunLoopAddObserver(rlp, observerRef, kCFRunLoopDefaultMode);
} void myRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
    NSLog(@"%lu-%@",activity,info);
}

三、RunLoop 內(nèi)部邏輯

蘋果文檔: The Run Loop Sequence of Events

image

RunLoop 核心流程源碼如下,比較長(zhǎng)可以粘下來(lái)再看.

 1 /* rl, rlm are locked on entrance and exit */
 2 /**
 3  *  運(yùn)行run loop
 4  *
 5  *  @param rl                         運(yùn)行的RunLoop對(duì)象
 6  *  @param rlm                       運(yùn)行的mode
 7  *  @param seconds                run loop超時(shí)時(shí)間
 8  *  @param stopAfterHandle     true:run loop處理完事件就退出  false:一直運(yùn)行直到超時(shí)或者被手動(dòng)終止
 9  *  @param previousMode        上一次運(yùn)行的mode
 10  *
 11  *  @return 返回4種狀態(tài)
 12  */
 13 static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) { 14     
 15     // 獲取系統(tǒng)啟動(dòng)后的CPU運(yùn)行時(shí)間,用于控制超時(shí)時(shí)間
 16     uint64_t startTSR = mach_absolute_time(); 17     
 18     // 判斷當(dāng)前runloop的狀態(tài)是否關(guān)閉
 19     if (__CFRunLoopIsStopped(rl)) { 20         __CFRunLoopUnsetStopped(rl);
 21         return kCFRunLoopRunStopped; 22     } else if (rlm->_stopped) {
 23         return kCFRunLoopRunStopped; 24         rlm->_stopped = false;
 25     }
 26     
 27     // mach端口,在內(nèi)核中,消息在端口之間傳遞。 初始為0
 28     mach_port_name_t dispatchPort = MACH_PORT_NULL; 29     // 判斷是否為主線程
 30     Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ))); 31     // 如果在主線程 && runloop是主線程的runloop && 該mode是commonMode,則給mach端口賦值為主線程收發(fā)消息的端口
 32     if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF(); 33     
 34 #if USE_DISPATCH_SOURCE_FOR_TIMERS
 35     mach_port_name_t modeQueuePort = MACH_PORT_NULL; 36     if (rlm->_queue) {
 37         // mode賦值為dispatch端口_dispatch_runloop_root_queue_perform_4CF
 38         modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
 39         if (!modeQueuePort) {
 40             CRASH("Unable to get port for run loop mode queue (%d)", -1);
 41         }
 42     }
 43 #endif
 44     
 45     dispatch_source_t timeout_timer = NULL; 46     struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
 47     if (seconds <= 0.0) { // instant timeout
 48         seconds = 0.0;
 49         timeout_context->termTSR = 0ULL; 50     } else if (seconds <= TIMER_INTERVAL_LIMIT) { 51         // seconds為超時(shí)時(shí)間,超時(shí)時(shí)執(zhí)行__CFRunLoopTimeout函數(shù)
 52         dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_OVERCOMMIT); 53         timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
 54         dispatch_retain(timeout_timer);
 55         timeout_context->ds = timeout_timer; 56         timeout_context->rl = (CFRunLoopRef)CFRetain(rl); 57         timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds); 58         dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context
 59         dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
 60         dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
 61         uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL); 62         dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);
 63         dispatch_resume(timeout_timer);
 64     } else { // infinite timeout 65         // 永不超時(shí)
 66         seconds = 9999999999.0;
 67         timeout_context->termTSR = UINT64_MAX; 68     }
 69     
 70     // 標(biāo)志位默認(rèn)為true
 71     Boolean didDispatchPortLastTime = true;
 72     //記錄最后runloop狀態(tài),用于return
 73     int32_t retVal = 0;
 74     do { 75         // 初始化一個(gè)存放內(nèi)核消息的緩沖池
 76         uint8_t msg_buffer[3 * 1024];
 77 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
 78         mach_msg_header_t *msg = NULL; 79         mach_port_t livePort = MACH_PORT_NULL; 80 #elif DEPLOYMENT_TARGET_WINDOWS
 81         HANDLE livePort = NULL; 82         Boolean windowsMessageReceived = false;
 83 #endif
 84         // 取所有需要監(jiān)聽的port
 85         __CFPortSet waitSet = rlm->_portSet;
 86         
 87         // 設(shè)置RunLoop為可以被喚醒狀態(tài)
 88         __CFRunLoopUnsetIgnoreWakeUps(rl);
 89         
 90         /// 2\. 通知 Observers: RunLoop 即將觸發(fā) Timer 回調(diào)。
 91         if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers); 92         if (rlm->_observerMask & kCFRunLoopBeforeSources) 93             /// 3\. 通知 Observers: RunLoop 即將觸發(fā) Source0 (非port) 回調(diào)。
 94             __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
 95         
 96         /// 執(zhí)行被加入的block
 97         __CFRunLoopDoBlocks(rl, rlm);
 98         /// 4\. RunLoop 觸發(fā) Source0 (非port) 回調(diào)。
 99         Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle); 100         if (sourceHandledThisLoop) { 101             /// 執(zhí)行被加入的block
102 __CFRunLoopDoBlocks(rl, rlm); 103 } 104         
105         // 如果沒(méi)有 Sources0 事件處理 并且 沒(méi)有超時(shí),poll為false 106         // 如果有 Sources0 事件處理 或者 超時(shí),poll都為true
107         Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR); 108         // 第一次do..whil循環(huán)不會(huì)走該分支,因?yàn)閐idDispatchPortLastTime初始化是true
109         if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) { 110 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
111             // 從緩沖區(qū)讀取消息
112             msg = (mach_msg_header_t *)msg_buffer; 113             /// 5\. 如果有 Source1 (基于port) 處于 ready 狀態(tài),直接處理這個(gè) Source1 然后跳轉(zhuǎn)去處理消息。
114             if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) { 115                 // 如果接收到了消息的話,前往第9步開始處理msg
116                 goto handle_msg; 117 } 118 #elif DEPLOYMENT_TARGET_WINDOWS
119             if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) { 120                 goto handle_msg; 121 } 122 #endif
123 } 124         
125         didDispatchPortLastTime = false; 126         /// 6.通知 Observers: RunLoop 的線程即將進(jìn)入休眠(sleep)。
127         if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting); 128         // 設(shè)置RunLoop為休眠狀態(tài)
129 __CFRunLoopSetSleeping(rl); 130         // do not do any user callouts after this point (after notifying of sleeping) 131         
132         // Must push the local-to-this-activation ports in on every loop 133         // iteration, as this mode could be run re-entrantly and we don't 134         // want these ports to get serviced.
135         
136 __CFPortSetInsert(dispatchPort, waitSet); 137         
138 __CFRunLoopModeUnlock(rlm); 139 __CFRunLoopUnlock(rl); 140         
141 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
142 #if USE_DISPATCH_SOURCE_FOR_TIMERS
143         
144         // 這里有個(gè)內(nèi)循環(huán),用于接收等待端口的消息 145         // 進(jìn)入此循環(huán)后,線程進(jìn)入休眠,直到收到新消息才跳出該循環(huán),繼續(xù)執(zhí)行run loop
146         do { 147             if (kCFUseCollectableAllocator) { 148                 objc_clear_stack(0); 149                 memset(msg_buffer, 0, sizeof(msg_buffer)); 150 } 151             
152             msg = (mach_msg_header_t *)msg_buffer; 153             /// 7.接收 waitSet 端口的消息
154             __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY); 155             // 收到消息之后,livePort的值為msg->msgh_local_port,
156             if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) { 157                 // Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
158                 while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue)); 159                 if (rlm->_timerFired) { 160                     // Leave livePort as the queue port, and service timers below
161                     rlm->_timerFired = false; 162                     break; 163                 } else { 164                     if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg); 165 } 166             } else { 167                 // Go ahead and leave the inner loop.
168                 break; 169 } 170         } while (1); 171 #else
172         if (kCFUseCollectableAllocator) { 173             objc_clear_stack(0); 174             memset(msg_buffer, 0, sizeof(msg_buffer)); 175 } 176         msg = (mach_msg_header_t *)msg_buffer; 177         /// 7\. 調(diào)用 mach_msg 等待接受 mach_port 的消息。線程將進(jìn)入休眠, 直到被下面某一個(gè)事件喚醒。 178         /// ? 一個(gè)基于 port 的Source 的事件。 179         /// ? 一個(gè) Timer 到時(shí)間了 180         /// ? RunLoop 自身的超時(shí)時(shí)間到了 181         /// ? 被其他什么調(diào)用者手動(dòng)喚醒
182         __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY); 183 #endif
184         
185         
186 #elif DEPLOYMENT_TARGET_WINDOWS
187         // Here, use the app-supplied message queue mask. They will set this if they are interested in having this run loop receive windows messages.
188         __CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived); 189 #endif
190         
191 __CFRunLoopLock(rl); 192 __CFRunLoopModeLock(rlm); 193         
194         // Must remove the local-to-this-activation ports in on every loop 195         // iteration, as this mode could be run re-entrantly and we don't 196         // want these ports to get serviced. Also, we don't want them left 197         // in there if this function returns.
198         
199 __CFPortSetRemove(dispatchPort, waitSet); 200         
201 __CFRunLoopSetIgnoreWakeUps(rl); 202         
203         // user callouts now OK again 204         //取消runloop的休眠狀態(tài)
205 __CFRunLoopUnsetSleeping(rl); 206         /// 8\. 通知 Observers: RunLoop 的線程剛剛被喚醒了。
207         if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting); 208         
209         /// 收到消息,處理消息。
210 handle_msg:; 211 __CFRunLoopSetIgnoreWakeUps(rl); 212         
213 #if DEPLOYMENT_TARGET_WINDOWS
214         if (windowsMessageReceived) { 215             // These Win32 APIs cause a callout, so make sure we're unlocked first and relocked after
216 __CFRunLoopModeUnlock(rlm); 217 __CFRunLoopUnlock(rl); 218             
219             if (rlm->_msgPump) { 220                 rlm->_msgPump(); 221             } else { 222 MSG msg; 223                 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) { 224                     TranslateMessage(&msg); 225                     DispatchMessage(&msg); 226 } 227 } 228             
229 __CFRunLoopLock(rl); 230 __CFRunLoopModeLock(rlm); 231             sourceHandledThisLoop = true; 232             
233             // To prevent starvation of sources other than the message queue, we check again to see if any other sources need to be serviced 234             // Use 0 for the mask so windows messages are ignored this time. Also use 0 for the timeout, because we're just checking to see if the things are signalled right now -- we will wait on them again later. 235             // NOTE: Ignore the dispatch source (it's not in the wait set anymore) and also don't run the observers here since we are polling.
236 __CFRunLoopSetSleeping(rl); 237 __CFRunLoopModeUnlock(rlm); 238 __CFRunLoopUnlock(rl); 239             
240             __CFRunLoopWaitForMultipleObjects(waitSet, NULL, 0, 0, &livePort, NULL); 241             
242 __CFRunLoopLock(rl); 243 __CFRunLoopModeLock(rlm); 244 __CFRunLoopUnsetSleeping(rl); 245             // If we have a new live port then it will be handled below as normal
246 } 247         
248         
249 #endif
250         if (MACH_PORT_NULL == livePort) { 251 CFRUNLOOP_WAKEUP_FOR_NOTHING(); 252             // handle nothing
253         } else if (livePort == rl->_wakeUpPort) { 254 CFRUNLOOP_WAKEUP_FOR_WAKEUP(); 255             // do nothing on Mac OS
256 #if DEPLOYMENT_TARGET_WINDOWS
257             // Always reset the wake up port, or risk spinning forever
258             ResetEvent(rl->_wakeUpPort); 259 #endif
260 } 261 #if USE_DISPATCH_SOURCE_FOR_TIMERS
262         else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) { 263 CFRUNLOOP_WAKEUP_FOR_TIMER(); 264             /// 9\. 處理 喚醒時(shí)收到的消息 265             /// 9.1 如果一個(gè) Timer 到時(shí)間了,觸發(fā)這個(gè)Timer的回調(diào)。
266             if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) { 267                 // Re-arm the next timer, because we apparently fired early
268 __CFArmNextTimerInMode(rlm, rl); 269 } 270 } 271 #endif
272 #if USE_MK_TIMER_TOO
273         else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) { 274 CFRUNLOOP_WAKEUP_FOR_TIMER(); 275             // On Windows, we have observed an issue where the timer port is set before the time which we requested it to be set. For example, we set the fire time to be TSR 167646765860, but it is actually observed firing at TSR 167646764145, which is 1715 ticks early. The result is that, when __CFRunLoopDoTimers checks to see if any of the run loop timers should be firing, it appears to be 'too early' for the next timer, and no timers are handled. 276             // In this case, the timer port has been automatically reset (since it was returned from MsgWaitForMultipleObjectsEx), and if we do not re-arm it, then no timers will ever be serviced again unless something adjusts the timer list (e.g. adding or removing timers). The fix for the issue is to reset the timer here if CFRunLoopDoTimers did not handle a timer itself. 9308754
277             if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) { 278                 // Re-arm the next timer
279 __CFArmNextTimerInMode(rlm, rl); 280 } 281 } 282 #endif
283         /// 9.2 如果有dispatch到main_queue的block,執(zhí)行block
284         else if (livePort == dispatchPort) { 285 CFRUNLOOP_WAKEUP_FOR_DISPATCH(); 286 __CFRunLoopModeUnlock(rlm); 287 __CFRunLoopUnlock(rl); 288             _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL); 289 #if DEPLOYMENT_TARGET_WINDOWS
290             void *msg = 0; 291 #endif
292 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg); 293             _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL); 294 __CFRunLoopLock(rl); 295 __CFRunLoopModeLock(rlm); 296             sourceHandledThisLoop = true; 297             didDispatchPortLastTime = true; 298         } else { 299             /// 9.3 如果一個(gè) Source1 (基于port) 發(fā)出事件了,處理這個(gè)事件
300 CFRUNLOOP_WAKEUP_FOR_SOURCE(); 301             // Despite the name, this works for windows handles as well
302             CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort); 303             if (rls) { 304 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
305                 mach_msg_header_t *reply = NULL; 306                 sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop; 307                 if (NULL != reply) { 308                     (void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); 309 CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply); 310 } 311 #elif DEPLOYMENT_TARGET_WINDOWS
312                 sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop; 313 #endif
314 } 315 } 316 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
317         if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg); 318 #endif
319         
320         /// 執(zhí)行加入到Loop的block
321 __CFRunLoopDoBlocks(rl, rlm); 322         
323         if (sourceHandledThisLoop && stopAfterHandle) { 324             /// 進(jìn)入loop時(shí)參數(shù)說(shuō)處理完事件就返回。
325             retVal = kCFRunLoopRunHandledSource; 326         } else if (timeout_context->termTSR < mach_absolute_time()) { 327             /// 超出傳入?yún)?shù)標(biāo)記的超時(shí)時(shí)間了
328             retVal = kCFRunLoopRunTimedOut; 329         } else if (__CFRunLoopIsStopped(rl)) { 330             /// 被外部調(diào)用者強(qiáng)制停止了
331 __CFRunLoopUnsetStopped(rl); 332             retVal = kCFRunLoopRunStopped; 333         } else if (rlm->_stopped) { 334             /// 自動(dòng)停止了
335             rlm->_stopped = false; 336             retVal = kCFRunLoopRunStopped; 337         } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) { 338             /// source/timer/observer一個(gè)都沒(méi)有了
339             retVal = kCFRunLoopRunFinished; 340 } 341         /// 如果沒(méi)超時(shí),mode里沒(méi)空,loop也沒(méi)被停止,那繼續(xù)loop。
342     } while (0 == retVal); 343     
344     if (timeout_timer) { 345 dispatch_source_cancel(timeout_timer); 346 dispatch_release(timeout_timer); 347     } else { 348 free(timeout_context); 349 } 350     
351     return retVal; 352 }            

通過(guò)這里也可驗(yàn)證上述“runloop 是一個(gè)對(duì)象,它提供了一個(gè)入口函數(shù),其內(nèi)部是一個(gè) do...while... 循環(huán),循環(huán)內(nèi) 進(jìn)行事件處理”。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Run Loop是什么 RunLoop顧名思義,是運(yùn)行循環(huán)。它跟線程是一一對(duì)應(yīng)的,每一個(gè)線程都有一個(gè)RunLoop...
    楚檳夕閱讀 3,105評(píng)論 0 2
  • 最近這幾天研究了下Runloop,下面就來(lái)分享一下心得(有不好的地方請(qǐng)幫忙指出來(lái),共同進(jìn)步,謝謝!!!) 一:前...
    Small_Potato閱讀 1,126評(píng)論 1 11
  • 一.NSRunLoop 在Cocoa中,每個(gè)線程(NSThread)對(duì)象中內(nèi)部都有一個(gè)run loop(NSRun...
    阿拉燈神釘閱讀 781評(píng)論 0 0
  • 在上一節(jié),簡(jiǎn)單的說(shuō)了一下Run Loop相關(guān)的知識(shí),這一節(jié)將做進(jìn)一步練習(xí)鞏固。 每個(gè)runloop對(duì)象都提供了一些...
    大鵬鳥閱讀 1,270評(píng)論 0 0
  • RUN Loop是什么? 1。runloop是事件接收和分發(fā)機(jī)制的一個(gè)實(shí)現(xiàn)。2。什么時(shí)候使用runloop?當(dāng)需要...
    且行且珍惜_iOS閱讀 872評(píng)論 1 5

友情鏈接更多精彩內(nèi)容