RunLoop實現(xiàn)學(xué)習(xí)記錄

注:以Core Foundation的實現(xiàn)作為參考(版本為CF-855.17)甸陌。

1. 相關(guān)數(shù)據(jù)結(jié)構(gòu)

1.1 run loop的基本結(jié)構(gòu)

CFRunLoopRef,是__CFRunLoop的結(jié)構(gòu)體指針:

typedef struct __CFRunLoop * CFRunLoopRef;

struct __CFRunLoop {
    CFRuntimeBase _base; // CF對象類型,相當(dāng)于OC對象的isa
    
    pthread_mutex_t _lock; // 訪問mode列表時保證同步
    __CFPort _wakeUpPort; // 線程被喚醒時使用 
    Boolean _unused;
    volatile _per_run_data *_perRunData; // 每次運行的相關(guān)狀態(tài)數(shù)據(jù)
    pthread_t _pthread; // 綁定的線程
    uint32_t _winthread;
    
    
    // 標(biāo)記為“common”的mode的名稱組成的集合
    CFMutableSetRef _commonModes;
    // “common”的mode對應(yīng)的統(tǒng)一item的集合
    CFMutableSetRef _commonModeItems;
    // runloop當(dāng)前所處的mode
    CFRunLoopModeRef _currentMode;
    // 所有的mode的集合
    CFMutableSetRef _modes;
    
    
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFTypeRef _counterpart;
};

作為RunLoop的基本數(shù)據(jù)結(jié)構(gòu)张抄,CFRunLoopRef內(nèi)部主要包含了:

  • _commonModes:標(biāo)記為“common”的mode的集合
  • _commonModeItems:所有“common”mode中共享的item,即修改此內(nèi)部的item,就會對所有的“common”的mode內(nèi)部的item進行更新谆构。
  • _currentMode:當(dāng)前所在的mode模式,切換時要退出重新指定
  • _modes:所有支持的mode框都。

常用API:

// 獲取當(dāng)前線程的run loop(延遲加載搬素,不訪問不創(chuàng)建)
CF_EXPORT CFRunLoopRef CFRunLoopGetCurrent(void);

// 獲取主線程的run loop(APP啟動后自動創(chuàng)建并運行)
CF_EXPORT CFRunLoopRef CFRunLoopGetMain(void);

既然引入了mode這一概念,我們就看看mode到底是個啥魏保。

1.2 RunLoop的Mode

RunLoop的mode為內(nèi)部數(shù)據(jù)熬尺,沒有對外公開。類型為CFRunLoopModeRef谓罗,即__CFRunLoopMode的結(jié)構(gòu)體指針粱哼。看一下簡化過的代碼結(jié)構(gòu):

typedef struct __CFRunLoopMode *CFRunLoopModeRef;

struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock; // 在對mode加鎖之前必須持有了run loop的鎖
    
    
    CFStringRef _name; // mode名稱檩咱,區(qū)分mode使用
    Boolean _stopped;
    char _padding[3];
    
    
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    
    
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet;
    CFIndex _observerMask;

    mach_port_t _timerPort;
    Boolean _mkTimerArmed;

    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR */
};

其中揭措,CFRunLoopModeRef中主要包含了如下幾個內(nèi)容:

  • _name:用于識別區(qū)分不同的mode實例胯舷。切換mode時可以使用。
  • _sources0:非基于端口的源的集合(我們在線程中自己添加的一般都是source0)
  • _sources1:基于端口的源的集合(通過Mach Port進行線程間通信使用)
  • _observers:觀察者的數(shù)組绊含,用于runloop在不同狀態(tài)時發(fā)送通知的接收對象桑嘶。
  • _timers:定時器數(shù)組,包含的就是NSTimer對象躬充。

常用API:

// 啟動run loop
CF_EXPORT void CFRunLoopRun(void);

// 以指定mode的方式啟動run loop
CF_EXPORT SInt32 CFRunLoopRunInMode(CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled);

// 將指定mode加入到run loop的commonMode數(shù)組中(加入的是modeName)
CF_EXPORT void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef mode);

下面我們分著來看各部分的數(shù)據(jù)結(jié)構(gòu)逃顶。

1.3 RunLoop源

CFRunLoopSourceRef,即__CFRunLoopSource結(jié)構(gòu)體的指針麻裳。其基本結(jié)構(gòu)如下:

typedef struct __CFRunLoopSource * CFRunLoopSourceRef;

struct __CFRunLoopSource {
    CFRuntimeBase _base;
    uint32_t _bits;
    pthread_mutex_t _lock;
    CFIndex _order; // 優(yōu)先級口蝠,不可變
    CFMutableBagRef _runLoops; // 綁定的runloop對象
    union {
        CFRunLoopSourceContext version0;    /* immutable, except invalidation */
        CFRunLoopSourceContext1 version1;   /* immutable, except invalidation */
    } _context;
};

其中,聯(lián)合體中的成員version0和version1即為上面mode中的source0和source1津坑。大概看一下二者的結(jié)構(gòu)體:

typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
    Boolean (*equal)(const void *info1, const void *info2);
    CFHashCode  (*hash)(const void *info);
    void    (*schedule)(void *info, CFRunLoopRef rl, CFStringRef mode);
    void    (*cancel)(void *info, CFRunLoopRef rl, CFStringRef mode);
    void    (*perform)(void *info);
} CFRunLoopSourceContext;


typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
    Boolean (*equal)(const void *info1, const void *info2);
    CFHashCode  (*hash)(const void *info);
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) || (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
    mach_port_t (*getPort)(void *info);
    void *  (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);
#else
    void *  (*getPort)(void *info);
    void    (*perform)(void *info);
#endif
} CFRunLoopSourceContext1;

可以看到妙蔗,二者內(nèi)容基本相同,都是包含了內(nèi)存管理疆瑰、相等性眉反、hash等信息。不同的是source1的結(jié)構(gòu)體(CFRunLoopSourceContext1)包含了mach_port相關(guān)函數(shù)穆役。

因此寸五,可以將source根據(jù)類型(version成員的0和1)分為source0和source1。

常用API:

// 創(chuàng)建source(傳入不同類型的context即可得到source0或source1)
CF_EXPORT CFRunLoopSourceRef CFRunLoopSourceCreate(CFAllocatorRef allocator, CFIndex order, CFRunLoopSourceContext *context);

// 向run loop的指定mode中添加source
CF_EXPORT void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);

// 從run loop的指定mode中移除source
CF_EXPORT void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);
1.4 Run Loop的定時器

定時器為CFRunLoopTimerRef類型耿币,也就是__CFRunLoopTimer的結(jié)構(gòu)體指針梳杏。

typedef struct __CFRunLoopTimer * CFRunLoopTimerRef;

struct __CFRunLoopTimer {
    CFRuntimeBase _base;
    uint16_t _bits;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop; // 綁定的run loop
    CFMutableSetRef _rlModes;
    CFAbsoluteTime _nextFireDate; // 下次觸發(fā)時間
    CFTimeInterval _interval; // 時間間隔
    CFTimeInterval _tolerance; // 寬容度
    uint64_t _fireTSR;          /* TSR units */
    CFIndex _order;         /* immutable */
    CFRunLoopTimerCallBack _callout; // 回調(diào)
    CFRunLoopTimerContext _context; /* immutable, except invalidation */
};

其中,主要包含了:

  • _runLoop:綁定到的run loop
  • _interval:執(zhí)行事件間隔(重復(fù)執(zhí)行的定時器)
  • _tolerance:寬容度淹接,也就是指定執(zhí)行時間的最大延遲時間
  • _callout:執(zhí)行的回調(diào)函數(shù)指針
  • _context:定時器上下文對象十性,包含內(nèi)存管理等相關(guān)函數(shù)
typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
} CFRunLoopTimerContext;

typedef void (*CFRunLoopTimerCallBack)(CFRunLoopTimerRef timer, void *info);

常用API:

// 以callback回調(diào)函數(shù)指針的形式創(chuàng)建timer
CF_EXPORT CFRunLoopTimerRef CFRunLoopTimerCreate(CFAllocatorRef allocator, CFAbsoluteTime fireDate, CFTimeInterval interval, CFOptionFlags flags, CFIndex order, CFRunLoopTimerCallBack callout, CFRunLoopTimerContext *context);

// 以block的形式創(chuàng)建timer
CF_EXPORT CFRunLoopTimerRef CFRunLoopTimerCreateWithHandler(CFAllocatorRef allocator, CFAbsoluteTime fireDate, CFTimeInterval interval, CFOptionFlags flags, CFIndex order, void (^block) (CFRunLoopTimerRef timer)) CF_AVAILABLE(10_7, 5_0);

// 向run loop的指定mode中添加timer
CF_EXPORT void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);

// 從run loop的指定mode中移除timer
CF_EXPORT void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
1.5 Run Loop的觀察者

觀察者為CFRunLoopObserverRef類型,即__CFRunLoopObserver的結(jié)構(gòu)體指針塑悼。

typedef struct __CFRunLoopObserver * CFRunLoopObserverRef;

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 */
};

其中劲适,主要包含:

  • _runLoop:對應(yīng)的run loop
  • _activities:用于監(jiān)聽的run loop的指定運行狀態(tài)。
  • _order:優(yōu)先級
  • _callout:收到監(jiān)聽通知的回調(diào)函數(shù)指針厢蒜。
  • _context:上下文對象

activities霞势,即CFOptionFlags,用于識別run loop在不同執(zhí)行時刻的運行狀態(tài):

/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    /** 進入loop */
    kCFRunLoopEntry = (1UL << 0),
    /** 準(zhǔn)備執(zhí)行timer */
    kCFRunLoopBeforeTimers = (1UL << 1),
    /** 準(zhǔn)備執(zhí)行source */
    kCFRunLoopBeforeSources = (1UL << 2),
    /** 線程準(zhǔn)備進入休眠 */
    kCFRunLoopBeforeWaiting = (1UL << 5),
    /** 線程剛被喚醒斑鸦,還沒有進行任務(wù)處理 */
    kCFRunLoopAfterWaiting = (1UL << 6),
    /** 退出loop */
    kCFRunLoopExit = (1UL << 7),
    
    /** 所有的狀態(tài) */
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

上下文對象為CFRunLoopObserverContext結(jié)構(gòu)體愕贡,內(nèi)部包含了內(nèi)存管理的相關(guān)函數(shù)指針:

typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
} CFRunLoopObserverContext;

回調(diào)函數(shù)的類型為:

typedef void (*CFRunLoopObserverCallBack)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);

常用API:

// 創(chuàng)建新的run loop觀察者,使用callback回調(diào)函數(shù)指針的方式
CF_EXPORT CFRunLoopObserverRef CFRunLoopObserverCreate(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, CFRunLoopObserverCallBack callout, CFRunLoopObserverContext *context);

// 創(chuàng)建新的run loop觀察者巷屿,使用block的方式
CF_EXPORT CFRunLoopObserverRef CFRunLoopObserverCreateWithHandler(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, void (^block) (CFRunLoopObserverRef observer, CFRunLoopActivity activity)) CF_AVAILABLE(10_7, 5_0);

// 向run loop的指定mode中添加觀察者
CF_EXPORT void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef mode);

// 從run loop的指定mode中移除觀察者
CF_EXPORT void CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef mode);

2. 重點API基本實現(xiàn)

2.1 CFRunLoopGetMain & CFRunLoopGetCurrent

系統(tǒng)隱藏了生成過程固以,通過獲取線程對應(yīng)的runloop對象,即可完成創(chuàng)建攒庵。

// 獲取主線程的runloop
CFRunLoopRef CFRunLoopGetMain(void) {
    CHECK_FOR_FORK();
    static CFRunLoopRef __main = NULL; // no retain needed
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
    return __main;
}

// 獲取當(dāng)前線程的runloop
CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    return _CFRunLoopGet0(pthread_self());
}

可以看到嘴纺,真正的實現(xiàn)都在 _CFRunLoopGet0 函數(shù)中:

static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFSpinLock_t loopsLock = CFSpinLockInit;

CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    // t為0,即為主線程
    if (pthread_equal(t, kNilPthreadT)) {
        t = pthread_main_thread_np();
    }
    // 獲取鎖
    __CFSpinLock(&loopsLock);
    
    // 懶加載浓冒,第一次調(diào)用為主線程栽渴,創(chuàng)建全局字典,并將新創(chuàng)建的主線程runloop加入其中
    if (!__CFRunLoops) {
        // 釋放鎖
        __CFSpinUnlock(&loopsLock);
        
        // 創(chuàng)建字典容器稳懒,用于存儲runloop
        CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
        
        // 創(chuàng)建主線程的runloop
        CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
        // 插入到字典中(key為線程指針)
        CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
        
        // __CFRunLoops全局字典指針指向此字典容器
        if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
            CFRelease(dict);
        }
        CFRelease(mainLoop);
        
        // 再次獲取鎖
        __CFSpinLock(&loopsLock);
    }
    
    
    // 從全局字典容器中獲取指定runloop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    // 釋放鎖
    __CFSpinUnlock(&loopsLock);
    
    // 懶加載
    if (!loop) {
        // 創(chuàng)建新runloop
        CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        
        __CFSpinLock(&loopsLock);
        
        loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
        if (!loop) {
            // 在全局字典容器中插入runloop
            CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
            loop = newLoop;
        }
        __CFSpinUnlock(&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;
}

通過實現(xiàn)可以看到:

  • runloop對象整體懶加載闲擦,不調(diào)用不生成。
  • runloop對象保存在全局字典中场梆,key為線程指針墅冷。
  • 系統(tǒng)第一次調(diào)用時,生成全局字典或油,且自動創(chuàng)建主線程的runloop寞忿,插入其中,因此APP可以保持運行顶岸。
2.2 CFRunLoopRun

首先看一下run loop的啟動過程:

void CFRunLoopRun(void) {   /* DOES CALLOUT */
    int32_t result;
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}

在以上兩個啟動run loop的函數(shù)中腔彰,真正的實現(xiàn)都包含在CFRunLoopRunSpecific中,且此函數(shù)會返回run loop的執(zhí)行狀態(tài)(由CFRunLoopRunInMode()函數(shù)返回)辖佣。

enum {
    kCFRunLoopRunFinished = 1,
    kCFRunLoopRunStopped = 2,
    kCFRunLoopRunTimedOut = 3,
    kCFRunLoopRunHandledSource = 4
};

現(xiàn)在霹抛,我們看一下運行循環(huán),到底是如何循環(huán)的卷谈。

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    
    // 正在釋放杯拐,直接返回
    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
    
    // run loop獲取鎖
    __CFRunLoopLock(rl);
    
    // 根據(jù)指定modeName,獲取到mode對象
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    
    // 若不存在此mode世蔗,或內(nèi)部沒有mode item
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
        // 返回
        Boolean did = false;
        if (currentMode) __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopUnlock(rl);
        return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
    }
    
    // 設(shè)置新的perRunData端逼,返回舊值
    volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
    
    // 之前所處的mode
    CFRunLoopModeRef previousMode = rl->_currentMode;
    
    // 記錄為新的運行mode
    rl->_currentMode = currentMode;
    
    // 設(shè)置為已完成,待返回
    int32_t result = kCFRunLoopRunFinished;

    // 如果存在監(jiān)聽進入loop狀態(tài)的觀察者
    if (currentMode->_observerMask & kCFRunLoopEntry )
        // 通知觀察者凸郑,run loop已進入(新mode進入)
        __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    
    // ** 核心:切換運行新mode(循環(huán)在這里面實現(xiàn)) **
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    
    // 如果存在監(jiān)聽退出loop狀態(tài)的觀察者
    if (currentMode->_observerMask & kCFRunLoopExit )
        // 通知觀察者裳食,run loop已退出
        __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

    // 釋放mode的鎖
    __CFRunLoopModeUnlock(currentMode);
    
    // 恢復(fù)為原始的perRunData,恢復(fù)狀態(tài)
    __CFRunLoopPopPerRunData(rl, previousPerRun);
    
    // run loop的運行mode恢復(fù)為原mode
    rl->_currentMode = previousMode;
    
    // 釋放run loop自身的鎖
    __CFRunLoopUnlock(rl);
    
    return result;
}

CFRunLoopRunSpecific函數(shù)的整體運行芙沥,是一個“創(chuàng)建新現(xiàn)場 -- 執(zhí)行l(wèi)oop -- 恢復(fù)舊現(xiàn)場”的過程诲祸。

  1. 根據(jù)指定的modeName名稱,獲取run loop內(nèi)部保存的mode對象而昨。將其設(shè)置為currentMode救氯,然后創(chuàng)建新的perRunData進行設(shè)置。且如果存在進入循環(huán)的觀察者歌憨,發(fā)通知進行告知着憨。

其中,_per_run_data指針標(biāo)記為volatile务嫡,即告知編譯器要直接從原地址讀取值甲抖,而不是從寄存器中讀绕岣摹(防止多線程更改時不修改寄存器中的值導(dǎo)致的數(shù)據(jù)不一致問題)。__CFRunLoopPushPerRunData函數(shù)的內(nèi)部實現(xiàn)為:

typedef struct _per_run_data {
    uint32_t a;
    uint32_t b;
    uint32_t stopped;
    uint32_t ignoreWakeUps;
} _per_run_data;

CF_INLINE volatile _per_run_data *__CFRunLoopPushPerRunData(CFRunLoopRef rl) {
    volatile _per_run_data *previous = rl->_perRunData;
    rl->_perRunData = (volatile _per_run_data *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(_per_run_data), 0);
    rl->_perRunData->a = 0x4346524C;
    rl->_perRunData->b = 0x4346524C; // 'CFRL'
    rl->_perRunData->stopped = 0x00000000;
    rl->_perRunData->ignoreWakeUps = 0x00000000;
    return previous;
}

可以看到准谚,runloop的_perRunData成員每次都是創(chuàng)建固定的新值挫剑。用來管理runloop的相關(guān)設(shè)置(stop狀態(tài),是否允許喚醒等)

  1. 調(diào)用 __CFRunLoopRun 函數(shù)柱衔,此函數(shù)才是真正的運行循環(huán)過程。后面進行單獨說明哲戚。
  2. 運行循環(huán)執(zhí)行結(jié)束后,恢復(fù)現(xiàn)場顺少。將上一次的運行mode澳盐、_perRunData等數(shù)據(jù)進行恢復(fù)祈纯。且如果存在監(jiān)聽退出狀態(tài)的觀察者,發(fā)通知告知叼耙。

這里腕窥,我們看一下runloop是如何給觀察者發(fā)送通知的。

2.2 __CFRunLoopDoObservers
static void __CFRunLoopDoObservers() __attribute__((noinline));
static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity) { /* DOES CALLOUT */
    CHECK_FOR_FORK();

    // 查看mode中observers的個數(shù)
    CFIndex cnt = rlm->_observers ? CFArrayGetCount(rlm->_observers) : 0;
    // 不存在筛婉,直接返回
    if (cnt < 1) return;

    /* Fire the observers */
    STACK_BUFFER_DECL(CFRunLoopObserverRef, buffer, (cnt <= 1024) ? cnt : 1);
    // cnt超過1024后簇爆,開辟cnt個內(nèi)存空間,相當(dāng)于數(shù)組
    CFRunLoopObserverRef *collectedObservers = (cnt <= 1024) ? buffer : (CFRunLoopObserverRef *)malloc(cnt * sizeof(CFRunLoopObserverRef));
    CFIndex obs_cnt = 0;
    for (CFIndex idx = 0; idx < cnt; idx++) {
        // 依次取出observer
        CFRunLoopObserverRef rlo = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(rlm->_observers, idx);
        // 若observer中存在需要的activity
        if (0 != (rlo->_activities & activity) && __CFIsValid(rlo) && !__CFRunLoopObserverIsFiring(rlo)) {
            // retain后爽撒,插入數(shù)組中
            collectedObservers[obs_cnt++] = (CFRunLoopObserverRef)CFRetain(rlo);
        }
    }
    
    // 釋放mode的鎖
    __CFRunLoopModeUnlock(rlm);
    // 釋放run loop鎖
    __CFRunLoopUnlock(rl);
    
    // **** 由此可以看出入蛆,為了提高訪問效率,將所有的觀察者添加到額外的數(shù)組中硕勿,是為了釋放鎖后哨毁,其他對象的高效訪問。 ****
    
    
    for (CFIndex idx = 0; idx < obs_cnt; idx++) {
        // 從數(shù)組中依次取出observer
        CFRunLoopObserverRef rlo = collectedObservers[idx];
        // 對observer加鎖
        __CFRunLoopObserverLock(rlo);
        
        if (__CFIsValid(rlo)) {
            // 根據(jù)是否重復(fù)確定是否需要置為無效
            Boolean doInvalidate = !__CFRunLoopObserverRepeats(rlo);
            // 設(shè)置對應(yīng)位的值(標(biāo)記為正在調(diào)用)
            __CFRunLoopObserverSetFiring(rlo);
            // 釋放鎖
            __CFRunLoopObserverUnlock(rlo);
            
           // 調(diào)用觀察者對應(yīng)的回調(diào)方法(_callout)
            __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(rlo->_callout, rlo, activity, rlo->_context.info);
            
            // 需要置為無效源武,則釋放此observer
            if (doInvalidate) {
                CFRunLoopObserverInvalidate(rlo);
            }
            
            // 設(shè)置對應(yīng)位的值(標(biāo)記為沒有調(diào)用)
            __CFRunLoopObserverUnsetFiring(rlo);
        } else {
            // 觀察者對象錯誤扼褪,直接釋放鎖
            __CFRunLoopObserverUnlock(rlo);
        }
        
        // 釋放observer(數(shù)組不再保留observer)
        CFRelease(rlo);
    }
    // 釋放runloop和mode的鎖
    __CFRunLoopLock(rl);
    __CFRunLoopModeLock(rlm);

    // 創(chuàng)建了新數(shù)組,則釋放內(nèi)存
    if (collectedObservers != buffer) free(collectedObservers);
}
  1. 將指定run loop mode中的_observers取出粱栖,單獨放到一個指定數(shù)組中進行處理(超出1024個observer后要單獨開辟內(nèi)存)话浇。由于調(diào)用callback函數(shù)為同步操作,由可能會長時間鎖住runloop和mode闹究。故單獨處理幔崖,防止影響runloop的執(zhí)行效率。
  2. 將監(jiān)聽的activity對應(yīng)的observer插入到數(shù)組容器中。activity即上面說過的CFOptionFlags赏寇,也就是runloop運行過程中的各種監(jiān)控時間點吉嫩。
  3. 插入到數(shù)組容器時要對observer進行保留操作,使用后要對observer進行釋放操作嗅定,內(nèi)存管理要時刻謹記率挣。
  4. 在observer數(shù)組中依次取出露戒,通過 CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION 函數(shù)對observer的_callout進行函數(shù)調(diào)用。
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);
    }
    getpid(); // thwart tail-call optimization
}

主要就是直接進行函數(shù)調(diào)用(getpid神馬進程相關(guān)的荠锭,就不動了证九。愧怜。拥坛。)猜惋。

2.3 __CFRunLoopRun

前面說了著摔,__CFRunLoopRun個才是核心所在谍咆,真正的“l(fā)oop”在這里執(zhí)行卧波。

由于代碼過長港粱,我們分步驟看查坪。

2.3.1 其他(非do-while循環(huán))
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {

    // 1. 狀態(tài)判斷
    if (__CFRunLoopIsStopped(rl)) {
        // 如果runloop已經(jīng)stop(_perRunData中的stop的值)
        // 恢復(fù)stop為初值偿曙,返回已stop狀態(tài)
        __CFRunLoopUnsetStopped(rl);
        return kCFRunLoopRunStopped;
    } else if (rlm->_stopped) {
        // 若當(dāng)前mode已經(jīng)stop
        // 恢復(fù)stop為初值望忆,返回已stop狀態(tài)
        rlm->_stopped = false;
        return kCFRunLoopRunStopped;
    }
    
    // 2. dispatchPort的獲取
    
    // 派發(fā)端口稿壁,如果當(dāng)前runloop是主線程runloop傅是,則為主隊列port喧笔,否則為NULL
    mach_port_name_t dispatchPort = MACH_PORT_NULL;
    Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
    if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) {
        dispatchPort = _dispatch_get_main_queue_port_4CF();
    }
    
    
    // 3. 超時定時器的設(shè)置
    
    dispatch_source_t timeout_timer = NULL;
    struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
    if (seconds <= 0.0) { // instant timeout
        // 不設(shè)置超時的情況书闸,即運行完就散
        
        seconds = 0.0;
        timeout_context->termTSR = 0ULL;
        
    } else if (seconds <= TIMER_INTERVAL_LIMIT) {
        // 有超時梗劫,但是未達到上限的情況
        
        // 通過global隊列梳侨,添加一個超時的timer走哺,到時間喚醒線程丙躏,釋放runloop
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_OVERCOMMIT);
        timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
            dispatch_retain(timeout_timer);
        timeout_context->ds = timeout_timer;
        timeout_context->rl = (CFRunLoopRef)CFRetain(rl);
        timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);
        dispatch_set_context(timeout_timer, timeout_context);
            // source gets ownership of context
        dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
        dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
        uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL);
        dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);
        dispatch_resume(timeout_timer);
    } else { // infinite timeout
        // 超過上限,即無限運行時間的情況
        
        seconds = 9999999999.0;
        timeout_context->termTSR = UINT64_MAX;
    }
    
    
    
    int32_t retVal = 0;
    do {
        ...
    } while (0 == retVal);
    
    // retVal只要不為0废恋,則runloop結(jié)束循環(huán),退出



    if (timeout_timer) {
        // 存在超時定時器拟烫,移除
        dispatch_source_cancel(timeout_timer);
        dispatch_release(timeout_timer);
    } else {
        free(timeout_context);
    }

    return retVal;
}

非核心部分主要做了三件事:

  1. 對runloop和currentMode的運行狀態(tài)進行判斷硕淑,防止無畏的運行置媳。
  2. 根據(jù)情況半开,查看端口是否為GCD的主隊列端口(用于后面通過主線程自身端口調(diào)用mach_msg函數(shù),執(zhí)行主隊列的異步任務(wù))抓韩。
  3. 根據(jù)設(shè)置的超時時間谒拴,使用GCD添加一個定時器英上,超時時直接釋放runloop苍日。最后結(jié)尾時檢查移除此定時器相恃。
2.3.2 do-while運行循環(huán)
    int32_t retVal = 0;
    do {
        // 1. 根據(jù)運行狀態(tài)拦耐,進行預(yù)處理(發(fā)通知杀糯、執(zhí)行block固翰、準(zhǔn)備處理GCD事件畸颅、休眠)

        // 存在kCFRunLoopBeforeTimers的觀察者没炒,發(fā)通知
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        
        // 存在kCFRunLoopBeforeSources的觀察者送火,發(fā)通知
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);

        // 調(diào)用block的時機1
        __CFRunLoopDoBlocks(rl, rlm);

        // 執(zhí)行source0(手動source)
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            // 執(zhí)行source0后种吸,調(diào)用block(調(diào)用block的時機2)
            __CFRunLoopDoBlocks(rl, rlm);
        }

        // GCD的主隊列
        if (MACH_PORT_NULL != dispatchPort) {
            msg = (mach_msg_header_t *)msg_buffer;
            // 通過主隊列端口直接發(fā)送消息(內(nèi)部的source1消息坚俗,mach_msg執(zhí)行)猖败,進行處理
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) {
                goto handle_msg;
            }
        }

        // 存在監(jiān)聽kCFRunLoopBeforeWaiting的觀察者,發(fā)通知
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        
        
        msg = (mach_msg_header_t *)msg_buffer;
        // 嘗試通過mach_msg函數(shù)進入休眠狀態(tài)剧董,接收到消息后(即被喚醒)尉剩,返回true
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
                
        
        // 如果存在監(jiān)聽kCFRunLoopAfterWaiting的觀察者边涕,發(fā)通知
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);



        // 2. 處理消息部分(線程已被喚醒 或 直接進入處理)
        
        // ** 處理消息:**       
        handle_msg:;

        if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
            // timer喚醒的
            // 以當(dāng)前時間為時間點功蜓,執(zhí)行相應(yīng)的timer
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
        } else if (livePort == dispatchPort) {
            // GCD主隊列喚醒的(dispatchPort不為NULL式撼,即證明是主隊列喚醒的)
            // 執(zhí)行主隊列的任務(wù)block
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);            
        } else {
            // 其他線程的source1喚醒的
            // 根據(jù)livePort著隆,查詢獲取source1對象
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
            if (rls) {
                mach_msg_header_t *reply = NULL;
                // 執(zhí)行source1
                sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
            }
        } 
        
        if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
        
        // 執(zhí)行block的時機3
        __CFRunLoopDoBlocks(rl, rlm);
        
        
        // 3. 根據(jù)狀態(tài)設(shè)置retVal值弦赖,決定運行循環(huán)是否繼續(xù)
        
        if (sourceHandledThisLoop && stopAfterHandle) {
            // 單次執(zhí)行完source
            retVal = kCFRunLoopRunHandledSource;
        } else if (timeout_context->termTSR < mach_absolute_time()) {
            // runloop超時
            retVal = kCFRunLoopRunTimedOut;
        } else if (__CFRunLoopIsStopped(rl)) {
            // runloop已停止
            retVal = kCFRunLoopRunStopped;
        } else if (rlm->_stopped) {
            // runloop的對應(yīng)mode已停止
            retVal = kCFRunLoopRunStopped;
        } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
            // runloop執(zhí)行完成
            retVal = kCFRunLoopRunFinished;
        }
        
    } while (0 == retVal);

雖然代碼很多(已經(jīng)大幅度簡化)蹬竖,但實際上只是分為三部分:

  1. 根據(jù)運行狀態(tài)币厕,進行預(yù)處理:
    • 在不同運行狀態(tài)旦装,給觀察者發(fā)通知(準(zhǔn)備執(zhí)行timer阴绢、準(zhǔn)備執(zhí)行source)
    • 執(zhí)行block
    • 執(zhí)行配置到mode中的source0呻袭,成功后再次檢測執(zhí)行block。實現(xiàn)函數(shù)為 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION
    • 準(zhǔn)備處理GCD事件(通過mach_msg函數(shù)踪古,自身主隊列的port執(zhí)行伏穆,存在則直接去處理任務(wù))
    • 沒有其他事件時枕扫,通過mach_msg函數(shù),讓線程進入睡眠诗鸭,同時在接收消息后自動喚醒
  2. 處理消息部分(線程已被喚醒 或 直接進入處理)强岸,根據(jù)端口類型蝌箍,對執(zhí)行的工作進行區(qū)分:
    • 執(zhí)行timer:CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION,其入口函數(shù) __CFRunLoopDoTimers__CFRunLoopDoObservers 實現(xiàn)相似:先將符合要求(剛剛到達或者稍微過時一點的)的timer加入到數(shù)組中杂拨,防止長時間鎖住runloop弹沽,然后依次調(diào)用 __CFRunLoopDoTimer 贷币,也就是calling_out函數(shù)役纹,完成回調(diào)促脉。
    • 執(zhí)行GCD主隊列的異步任務(wù):CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
    • source1的主動喚醒任務(wù):mach_msg窒所,進入內(nèi)核態(tài)旁仿,直接進行線程通信
    • 再次檢測執(zhí)行block
  3. 根據(jù)狀態(tài)設(shè)置retVal值枯冈,決定運行循環(huán)是否繼續(xù)尘奏。如果狀態(tài)都不滿足炫加,仍為0俗孝,則繼續(xù)執(zhí)行循環(huán)驹针,保證線程不退出柬甥。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末苛蒲,一起剝皮案震驚了整個濱河市臂外,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嚎货,老刑警劉巖殖属,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洗显,死亡現(xiàn)場離奇詭異挠唆,居然都是意外死亡玄组,警方通過查閱死者的電腦和手機俄讹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進店門沽瞭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人城丧,你說我怎么就攤上這事亡哄∥霉撸” “怎么了截型?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵发钝,是天一觀的道長酝豪。 經(jīng)常有香客問我孵淘,道長夺英,這世上最難降的妖魔是什么痛悯? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任载萌,我火速辦了婚禮扭仁,結(jié)果婚禮上厅翔,老公的妹妹穿的比我還像新娘熊泵。我一直安慰自己甸昏,他們只是感情好卒蘸,可當(dāng)我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布缸沃。 她就那樣靜靜地躺著村缸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪武氓。 梳的紋絲不亂的頭發(fā)上梯皿,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天,我揣著相機與錄音县恕,去河邊找鬼东羹。 笑死,一個胖子當(dāng)著我的面吹牛忠烛,可吹牛的內(nèi)容都是我干的属提。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼美尸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤凉翻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后残炮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脉漏,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年叨襟,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡碴犬,死狀恐怖偿荷,靈堂內(nèi)的尸體忽然破棺而出贪嫂,到底是詐尸還是另有隱情赢织,我是刑警寧澤八毯,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一僧界、第九天 我趴在偏房一處隱蔽的房頂上張望葬荷。 院中可真熱鬧抛计,春花似錦波俄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽边篮。三九已至胜蛉,卻和暖如春君旦,著一層夾襖步出監(jiān)牢的瞬間扶欣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工忌卤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留牺弹,地道東北人趴梢。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像焦履,于是被迫代替她去往敵國和親躺翻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,969評論 2 355

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