RunLoop原始碼分析、基本使用場景和常見問題

Deft_MKJing宓珂璟發表於2016-12-25

YY大神的連結
goole ASDK的Runloop超流暢UI思路

Runloop

  • 從字面上看

    • 迴圈執行 內部核心其實就是一個do while迴圈
    • 跑圈 就和這個小夥子一樣
      和這個小夥子一樣跑圈
      BOOL running = YES;
      do{
          // 處理各種事件執行各種任務
      
      }while(running)
  • 基本作用
    • 保持程式的持續執行
    • 處理App中的各種事件(比如觸控事件、定時器事件、Selector事件)
    • 節省CPU資源,提高程式效能,做事的時候跑起來,不做的時候休眠

Runloop與執行緒

  • 每條執行緒都有唯一的一個與之對應的runloop物件
  • 主執行緒的runloop自動建立,子執行緒的runloop需要手動建立,不建立就沒有
  • Runloop在第一次獲取時建立,線上程結束時銷燬

獲取runloop物件

  • Fundation

    • [NSRunLoop currentRunLoop] // 獲取當前執行緒的
    • [NSRunLoop mainRunLoop]// 獲取主執行緒
  • core fundation

    • CFRunLoopGetCurrent();// 獲取當前執行緒
    • CFRunLoopGetMain(); // 獲取主執行緒

原始碼分析系列,當我們呼叫上面的程式碼時,來看看原始碼原始碼下載地址
1.入口

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

CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    return _CFRunLoopGet0(pthread_self());
}



2.看到_CFRunLoopGet0 這個函式沒,這就是第二部,內部實現

// 全域性字典,key是pthread_t value是CFRunLoopRef
static CFMutableDictionaryRef __CFRunLoops = NULL;
// 訪問__CFRunLoops時的鎖
static CFLock_t loopsLock = CFLockInit;

// 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);
        // 根據主執行緒建立主執行緒RunLoop
        CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
        // 加入到全域性字典 key main_thread value mainLoop
        CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
        // 和全域性__CFRunLoops關聯上,然後把臨時建立的殺死
        if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
            CFRelease(dict);
        }
        CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    // 如果全域性字典存在,根據執行緒取RunLoop
    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) {
            // 根據執行緒key把剛建立的關聯上
            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);
    }
    // 註冊一個回撥,當執行緒銷燬時,通知銷燬RunLoop
    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;
}



大致建立邏輯就是上面註釋,你不需要顯示alloc,你只需要獲取就行了,內部已經有建立的邏輯,沒錯,RunLoop物件就搞定了,下面來看看物件到底是什麼結構

RunLoop相關類

  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef
    這裡寫圖片描述

CFRunLoopModeRef代表RunLoop的執行模式

  • 一個 RunLoop 包含若干個 Mode,每個Mode又包含若干個Source/Timer/Observer
  • 每次RunLoop啟動時,只能指定其中一個 Mode,這個Mode被稱作 CurrentMode

  • 如果需要切換Mode,只能退出Loop,再重新指定一個Mode進入

  • 這樣做主要是為了分隔開不同組的Source/Timer/Observer,讓其互不影響

系統註冊了五個Mode(可以打斷點看呼叫棧)

1.kCFRunLoopDefaultMode:App的預設Mode,通常主執行緒實在這個Mode
2.UITrackingRunLoopMode: 頁面跟蹤Mode,用於滾動追蹤觸控滑動,保證頁面滑動式不受其他Mode影響
NSTimer預設是加到DefaultMode裡面的,當滾動式切換到這個模式,所以之前的Timer事件不再呼叫

3.UIInitializationRunLoopMode:在剛啟動App時第一次進入就是這個Mode,完成之後不再用
4.GSEventReveiveRunLoopMode:系統事件的內部Mode,通常用不到

5.CFRunLoopCommonModes:這個是一個佔位用的Mode,不是一個真正的Mode,
用來標示切換不同Mode時是否加有這個欄位的Mode還是能繼續接受事件

CFRunLoopSourceRef是事件源(輸入源)

理論分Source:

  • Port-Based Sources 基於Mach port核心/其他執行緒事件源
  • Custom Input Sources 自定義基本不用
  • Cocoa Perform Selector Sources [Self PerformSele…]

按呼叫棧分Source:

  • Source0:非基於Port
  • Source1:基於Port,通過核心和其他執行緒通訊,接受,分發系統事件

CFRunLoopTimerRef)

這個東西就是一個定時器,通過設定特定的時間間隔來給叫醒RunLoop處理事件

CFRunLoopObserverRef

CFRunLoopObserverRef是觀察者,能夠監聽RunLoop的狀態改變

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
        kCFRunLoopEntry = (1UL << 0),// 即將進入RunLoop 1
        kCFRunLoopBeforeTimers = (1UL << 1),// 即將處理Timer事件 2
        kCFRunLoopBeforeSources = (1UL << 2), // 即將處理事件源 4
        kCFRunLoopBeforeWaiting = (1UL << 5),// 即將進入休眠 32
        kCFRunLoopAfterWaiting = (1UL << 6),// 即將進入從休眠中醒來 64
        kCFRunLoopExit = (1UL << 7),// 退出 128
        kCFRunLoopAllActivities = 0x0FFFFFFFU // 上述所有模式監聽列舉
    };



上述提到的Source/Timer/OBserver就是一個ModeItem,如果一個Mode中一個Item都沒有,RunLoop會自動退出,這就是後面要提到的常駐執行緒建立方式,先看看RunLoop和Mode兩者的結構,然後再舉例子對上面的行為進行分析

struct __CFRunLoop {
        CFRuntimeBase _base;
        CFMutableSetRef _commonModes; // 名字Default/Tracking
        CFMutableSetRef _commonModeItems;// Timer/Source/Observer
        CFRunLoopModeRef _currentMode;// 當前Mode
        CFMutableSetRef _modes;//一個RunLoop下有多個Mode
    };

    struct __CFRunLoopMode {
        CFRuntimeBase _base;
        pthread_mutex_t _lock;  /* must have the run loop locked before locking this */
        CFStringRef _name; // 用來存放到RunLoop中_commonModes/_modes的欄位
        Boolean _stopped;
        char _padding[3];
        CFMutableSetRef _sources0;// 非port事件
        CFMutableSetRef _sources1;// port事件
        CFMutableArrayRef _observers;// 觀察者
        CFMutableArrayRef _timers;// timer
        ......
    }



個人理解下,RunLoop結構體中有個modes用來存放所有可切換的Mode,例如Default/Tracking等,currentMode就指定當前在運作的Mode,CommandModes就是存放著無論你切換哪個Mode,你都需要同步_commonModeItems下所有事件的ModeName,你可以把Mode看做一個個的屬於你自己的任務,而commentMode看做每個人都要做的公共任務,當你有公共任務的時候也就是棧頂有Commentmode的時候,你做自己的子線任務的時候,同時需要做棧頂的公共任務

例項1Timer

// 方法1
    //  on the current run loop in the default mode
    // 建立的時候是加到NSDefaultRunLoopMode中的
//    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    // 方法2
    // 其實和方法一是一樣的,只是展開來寫罷了
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.3 target:self selector:@selector(run) userInfo:nil repeats:YES];
//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

    // 為了在滾動的時候和不滾動的時候都進行呼叫NSTimer
    // 只需要把Timer加到當前RunLoop中的CommonModes Set容器裡面去就好了,無論切換什麼模式,都會同步該Timer
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];



預設情況下我們建立的Timer只會在Default狀態下執行,當我們滾動的時候Timer就會不呼叫事件,原因就是RunLoop中它預設加到了NSDefaultMode中,當滾動的時候切換到NSTrackingMode的時候,NSTimer自然就不執行了,這是因為RunLoop只會在一個Mode下執行,當切換Mode的時候需要重啟Loop,而且事件不會同步,除非你把事件加到棧頂的CommonModes裡面去,這樣就可以在當前RunLoop下的所有模式下共享執行了

例項2Observer,可以在事件處理前進行一些攔截

//    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
//        kCFRunLoopEntry = (1UL << 0),// 即將進入RunLoop 1
//        kCFRunLoopBeforeTimers = (1UL << 1),// 即將處理Timer事件 2
//        kCFRunLoopBeforeSources = (1UL << 2), // 即將處理事件源 4
//        kCFRunLoopBeforeWaiting = (1UL << 5),// 即將進入休眠 32
//        kCFRunLoopAfterWaiting = (1UL << 6),// 即將進入從休眠中醒來 64
//        kCFRunLoopExit = (1UL << 7),// 退出 128
//        kCFRunLoopAllActivities = 0x0FFFFFFFU // 上述所有模式監聽列舉
//    };
    CFRunLoopObserverRef observe = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        if (activity == kCFRunLoopExit) {
            NSLog(@"即將退出runloop%lu",activity);
        }
        else if (activity == kCFRunLoopBeforeWaiting)
        {
            NSLog(@"即將進入休眠%lu",activity);

        }
        else if (activity == kCFRunLoopAfterWaiting)
        {
            NSLog(@"剛從休眠中喚醒%lu",activity);

        }else if (activity == kCFRunLoopBeforeSources)
        {
            NSLog(@"即將處理 Source%lu",activity);

        }else if (activity == kCFRunLoopBeforeTimers)
        {
            NSLog(@"即將處理 Timer%lu",activity);

        }
        else if (activity == kCFRunLoopEntry)
        {
            NSLog(@"即將進入Loop%lu",activity);

        }


    });

    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observe, kCFRunLoopDefaultMode);

    /*
     CF的記憶體管理(Core Foundation)
     1.凡是帶有Create、Copy、Retain等字眼的函式,建立出來的物件,都需要在最後做一次release
     * 比如CFRunLoopObserverCreate
     2.release函式:CFRelease(物件);
     */
    CFRelease(observe);

呼叫棧

例項3 AFN2x和YYKit下 的常駐執行緒

// YYKit常駐執行緒
/// Network thread entry point.
+ (void)_networkThreadMain:(id)object {
    // 常規做法最外層也用autorelease包起來
    @autoreleasepool {
        [[NSThread currentThread] setName:@"com.ibireme.yykit.webimage.request"];
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        // 給建立的loop新增任意一個item
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}

/// Global image request network thread, used by NSURLConnection delegate.
+ (NSThread *)_networkThread {
    static NSThread *thread = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        thread = [[NSThread alloc] initWithTarget:self selector:@selector(_networkThreadMain:) object:nil];
        if ([thread respondsToSelector:@selector(setQualityOfService:)]) {
            thread.qualityOfService = NSQualityOfServiceBackground;
        }
        [thread start];
    });
    return thread;
}



這裡的做法是建立 一個RunLoop,但是如果沒有新增任何Obserer/Timer/Source,Runloop預設你是會退出的,要進入迴圈必須加一個item

RunLoop整體邏輯

這裡寫圖片描述
這裡寫圖片描述

這個圖的前提需要注意的是,需要檢查ModeItem是否為空,是的話直接退出Loop

原始碼分析

//1.直接呼叫Run方法
void CFRunLoopRun(void) {   /* DOES CALLOUT */
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);

    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
// 通過指定Mode呼叫Run方法
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
// 2.啟動
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    //3. 根據RunloopModeName找到Mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    //4. 如果Mode為空裡面沒有任何的item直接返回
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
    return kCFRunLoopRunFinished;
    }
    //5. 如果不為空 先通知Observer:即將進入Loop kCFRunLoopEntry = (1UL << 0),// 即將進入RunLoop 1
    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    //6. 進入核心函式進行跑圈loop
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    //end: kCFRunLoopExit = (1UL << 7),// 退出 128
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
    return result;
}
// 核心跑圈程式碼
/* rl, rlm are locked on entrance and exit */
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    uint64_t startTSR = mach_absolute_time();
    __CFPortSet waitSet = rlm->_portSet;

        __CFRunLoopUnsetIgnoreWakeUps(rl);
        // 8.通知Observer
        // kCFRunLoopBeforeTimers = (1UL << 1),// 即將處理Timer事件 2
        // kCFRunLoopBeforeSources = (1UL << 2), // 即將處理事件源 4

        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
    // 任務加入Block處理
    __CFRunLoopDoBlocks(rl, rlm);
        //9. 處理Source0事件 執行事件加入block
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            __CFRunLoopDoBlocks(rl, rlm);
    }

        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);

        //10. 如果有source1進來,基於port的訊息進來 處理Source1 goto語句跳轉到下面
        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, &voucherState, NULL)) {
                // 跳轉
                goto handle_msg;
            }
        }

        didDispatchPortLastTime = false;
    //11. 通知ObserverseRunloop即將進入休眠kCFRunLoopBeforeWaiting
    if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);

#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#if USE_DISPATCH_SOURCE_FOR_TIMERS
        do {
           //12. 呼叫mach方法等待訊息的接受,執行緒進入休眠,當port傳來source1的時候,當Timer時間到了,當Loop超時了
           // 當被顯示喚醒的時候
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);

        } while (1);
    //13. 通知Runloop執行緒被剛剛喚醒
    if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
    //14. 處理訊息
        handle_msg:;
        __CFRunLoopSetIgnoreWakeUps(rl);

//15. 如果Timer到了 觸發Timer回撥
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
//16. 如果是由dispatch到main_queue的block,執行block
            CFRUNLOOP_WAKEUP_FOR_DISPATCH();
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);

//17. 如果一個source1發出事件,處理source1
            CFRUNLOOP_WAKEUP_FOR_SOURCE();
            // Despite the name, this works for windows handles as well
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
        sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
        sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
    __CFRunLoopDoBlocks(rl, rlm);

//18.處理是否繼續迴圈 
    if (sourceHandledThisLoop && stopAfterHandle) {
    // 進入loop時引數說處理完事件就返回。
        retVal = kCFRunLoopRunHandledSource;
        } else if (timeout_context->termTSR < mach_absolute_time()) {
    // 超時
            retVal = kCFRunLoopRunTimedOut;
    } else if (__CFRunLoopIsStopped(rl)) {
    // 外部強制停止
            __CFRunLoopUnsetStopped(rl);
        retVal = kCFRunLoopRunStopped;
    } else if (rlm->_stopped) {
        rlm->_stopped = false;
        retVal = kCFRunLoopRunStopped;
    } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
    // source/timer/observer為空一個都沒了
        retVal = kCFRunLoopRunFinished;
    }
    } while (0 == retVal);// 還是0,繼續跑

    return retVal;
}

關於Runloop的一些問題

  • 什麼是RunLoop?
    內部就是一個do - While迴圈,在這個迴圈內部不斷處理各種事件
    (Source、Timer、Observer)
    一個執行緒對應一個RunLoop,主執行緒RunLoop預設已經啟動,子執行緒的RunLoop得手動啟動
    RunLoop只能選擇一個模式,如果當前模式中沒有Source、Timer、Obeserver直接突出

  • 你在開發中怎麼使用RunLoop?
    1.AF2x.和YYKit常駐執行緒
    在子執行緒中開啟定時器
    子執行緒中長期監控一些行為(掃描網路、沙盒、語音監控)

    2.可以控制定時器在特定模式下執行

    3.可以讓某些事件在特定模式下執行(Performance…inmodes(mode引數))
    4.可以新增Observer監聽RunLoop的狀態,比如監聽點選事件處理前處理某些事情

  • 自動釋放池什麼時候釋放
    在RunLoop睡眠之前釋放(kCFRunLoopBeforeWaiting)

總結

4.什麼是Runloop

Runloop他的本質就是一個do,while迴圈
(1)保持程式的持續執行,當有事做時做事,
(2)負責監聽處理App中的各種事件(比如觸控事件、定時器事件、Selector事件)
(3)節省CPU資源,提高程式效能,做事的時候跑起來,不做的時候休眠。

每個執行緒都有一個Run Loop,主執行緒的Run Loop會在App執行時自動執行,子執行緒中需要手動獲取執行,建立是發生在第一次獲取時,RunLoop 的銷燬是發生線上程結束時。

一個 RunLoop 包含若干個 Mode,每個 Mode 又包含若干個 Source/Timer/Observer。每次呼叫 RunLoop 的主函式時,只能指定其中一個 Mode,這個Mode被稱作 CurrentMode。如果需要切換 Mode,只能退出 Loop,再重新指定一個 Mode 進入。這樣做主要是為了分隔開不同組的 Source/Timer/Observer,讓其互不影響。當建立的loop,沒有任何上面的屬性,就會直接退出。
Run的時候必須制定mode,那麼這裡有個知識點是CommonMode,不能指定該Mode進行執行,但是可以把事件標記到該屬性,那麼無論切換到哪個Mode都會執行

Apple應用
1.AutoreleasePool
App啟動後,蘋果在主執行緒 RunLoop 裡註冊了兩個 Observer,第一個 Observer 監視的事件是 Entry(即將進入Loop),執行方法去建立pool,優先順序最高,第二個是BeforeWaiting(準備進入休眠) 時呼叫pop and push,先清除舊pool,然後建立新pool,或者再退出的時候釋放自動釋放池子,優先順序最低

2.啟動時註冊,處理事件,nstimer,UI,渲染

3.網路請求框架下的執行緒通訊

開發者應用:
1.常駐執行緒 處理網路請求的回撥
2.Common在不同模式下也執行NSTimer
3.新增Observe屬性的觀察,在即將進入休眠的ASDK的原理,以下就是AS框架的Runloop應用,這裡把排版,繪製等操作放到非同步做,然後儲存起來
在kCFRunLoopBeforeWaiting和kCFRunLoopExit兩個activity的回撥中將之前非同步完成的工作同步到主執行緒中去。
ASDK goole的UI框架原理
一個自稱用cell實現Runloop應用的人

__unsafe_unretained __typeof__(self) weakSelf = self;
    void (^handlerBlock) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
      [weakSelf processQueue];
    };
    _runLoopObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, 0, handlerBlock);
    CFRunLoopAddObserver(_runLoop, _runLoopObserver,  kCFRunLoopCommonModes);

結構:

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

相關文章