iOS進(jìn)階01:Runloop

一、Runloop的定義

Runloop就是運(yùn)行時(shí)循環(huán),保證程序一直運(yùn)行下去

  • Runloop實(shí)際上是一個(gè)對(duì)象,這個(gè)對(duì)象用于處理程序運(yùn)行過(guò)程中遇到的各種事件(觸摸,UI刷新,定時(shí)器,performSelector等等),保證程序的持續(xù)運(yùn)行
  • Runloop在沒(méi)有事件需要處理的時(shí)候,會(huì)使線程進(jìn)入休眠狀態(tài),從而減少CPU的消耗,提高程序性能。
  • Runloop是一種消息機(jī)制的處理模式

二、 Runloop的作用

  • 保證程序持續(xù)運(yùn)行
  • 喚醒線程,響應(yīng)程序的使用
  • 在沒(méi)有事件需要處理的時(shí)候,使線程進(jìn)入休眠狀態(tài),減少CPU的消耗。

三、Runloop與線程的關(guān)系

  • 線程與Runloop是一一對(duì)應(yīng)的關(guān)系

從代碼doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode))看出:
當(dāng)前runloop模式等于傳入的model,或者是runloop模式是組合模式kCFRunLoopCommonModes,doit=YES,就會(huì)執(zhí)行。
這也是為什么NSTimer加入到kCFRunLoopCommonModes模式下的runloop時(shí),不會(huì)因?yàn)轫?yè)面滑動(dòng)造成定時(shí)器延遲的原因了。

static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // Call with rl and rlm locked
    // ...
    if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {
        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) {
                __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;
}

四、Runloop應(yīng)用

1. 使用Runloop來(lái)監(jiān)控程序的卡頓

參考文章:
深入理解RunLoop
iOS監(jiān)控卡頓

原理:
1.需要?jiǎng)?chuàng)建一個(gè)CFRunLoopObserverContext觀察者,然后將觀察者runLoopObserver添加到主線程 RunLoop的common模式下觀察
2.創(chuàng)建一個(gè)持續(xù)的子線程專門(mén)用來(lái)監(jiān)控主線程的RunLoop狀態(tài)
3.重點(diǎn)關(guān)注RunLoop 在進(jìn)入睡眠之前和喚醒后的兩個(gè) loop狀態(tài)定義的值分別是 kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting,如果停留在這兩個(gè)狀態(tài)的連續(xù)超時(shí)50ms,則認(rèn)為是卡頓。

static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
    SeMonitorController *instrance = [SeMonitorController sharedInstance];
    instrance->_activity = activity;
    // 發(fā)送信號(hào)
    dispatch_semaphore_t semaphore = instrance->_semaphore;
    dispatch_semaphore_signal(semaphore);
}

- (void)registerObserver
{
    CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL};
    _observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
                                                            kCFRunLoopAllActivities,
                                                            YES,
                                                            0,
                                                            &runLoopObserverCallBack,
                                                            &context);
    CFRunLoopAddObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes);
   // 創(chuàng)建信號(hào)

    _semaphore = dispatch_semaphore_create(0);
    // 在子線程監(jiān)控時(shí)長(zhǎng)
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (YES) {
            // 假定連續(xù)5次超時(shí)50ms認(rèn)為卡頓(當(dāng)然也包含了單次超時(shí)250ms)
            long st = dispatch_semaphore_wait(_semaphore, dispatch_time(DISPATCH_TIME_NOW, 0.05f*NSEC_PER_MSEC));
            if (st != 0){
                if (_activity==kCFRunLoopBeforeSources || _activity==kCFRunLoopAfterWaiting)
                {
                    if (++_countTime < 5)   continue;
                     
                    NSLog(@"something lag");
                }
            }
            _countTime = 0;
        }
    });
}
2. 使用CADisplayLink來(lái)監(jiān)控FPS

// 屏幕刷新頻率
// 將CADisplayLink添加到主Runloop中,來(lái)監(jiān)控主線的屏幕刷新頻率

_link = [CADisplayLink displayLinkWithTarget:weakself selector:@selector(tick:)];
[_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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