白話文-RunLoop

沒有runloop 就意味著app一運行就會退出(換句話說,runloop保護著app不會被退出)

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

  • 和線程是1對1的(存在于一個全局的map中, 線程作為key, runLoop 作為value)
  • 線程中默認是沒有runloop的(主線程在AppDelegate中會自動獲取(創(chuàng)建) )
  • 通過[NSRunLoop currentRunLoop];創(chuàng)建runLoop ;
  • 通過[NSRunLoop mainRunLoop];獲取主線程runLoop ;
  • Runloop會在線程結(jié)束的時候銷毀

RunLoop創(chuàng)建源碼分析

  • 注意閱讀注釋
[CFRunLoop currentRunLoop];

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

static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFLock_t loopsLock = CFLockInit;
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    // t : 傳入的當(dāng)前線程
    if (pthread_equal(t, kNilPthreadT)) {
    t = pthread_main_thread_np();
    }
    __CFLock(&loopsLock);
    // __CFRunLoops 靜態(tài)map 初始化為null
    if (!__CFRunLoops) {
    // 創(chuàng)建全局 runloopMap和主線程的runloop,
        __CFUnlock(&loopsLock);
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
    CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
    CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
    // 將 dict 賦值給__CFRunLoops (搞不懂__CFRunLoops的可以看這里)
    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
        CFRelease(dict);
    }
    CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    // 使用 入?yún)⒌?t 在 全局的map中 獲取到響應(yīng)的runloop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    // 如果沒有獲取到runloop, 則新建一個runloop
    if (!loop) {
    // 新建一個
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
    // 再次獲取當(dāng)先線程runloop
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    if (!loop) {
        // 確定當(dāng)前線程沒有runloop 將上面新創(chuàng)建的newloop保存到全局的map中
        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;
}

可以自己創(chuàng)建監(jiān)聽來監(jiān)聽Runloop的狀態(tài)

// 通過block的方式回調(diào)
 CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"######進入kCFRunLoopEntry");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"######即將處理Timer事件");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"######即將處理Source事件");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"######即將休眠");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"######被喚醒");
                break;
            case kCFRunLoopExit:
                NSLog(@"######退出RunLoop");
                break;
            default:
                break;
        }
        CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
        NSLog(@"當(dāng)前runloop模式 - %@", mode);
        CFRelease(mode);
    });
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    CFRelease(observer);

RunLoop的應(yīng)用

定時器失效

Q: 定時器失效問題
A: 因為timer模式是工作在runloop的kCFRunLoopDefaultMode模式下,
當(dāng)滑動scorllView時候runloop會切換到UITrackingRunLoopMode模式下,所以導(dǎo)致timer定時器失效;
解決方法

// 可以將timer標記為CommonModes
[NSRunLoop currentRunLoop]addTimer:timer forMode:kCFRunLoopCommonModes];

線程?;?/p>

需要保護線程不退出可以給子線程添加runloop來保證線程不被退出
如果需要在適當(dāng)?shù)臅r候銷毀線程 runloop 一定不能通過 [runloop run] 方法開啟
PS: runloop提供的run方法是無法銷毀的,其專門用于開啟一個永不銷毀的線程
如果需要在適當(dāng)?shù)臅r候銷毀子線程, 就需要自己實現(xiàn)runloop的開啟方法

// 這一句 是在dealloc中執(zhí)行stop函數(shù)時防止對象已經(jīng)被釋放導(dǎo)致weakSelf為nil, 判斷失效導(dǎo)致runloop無法銷毀
// while(weakSelf && !wealSelf.isStop) 
while(!isStop) {
  [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
// 當(dāng)需要結(jié)束線程的時候 在相關(guān)的方法中 將 stop 標記為no 用以結(jié)束runloop 從而退出線程
-(void)stop{
     // 注意! waitUntilDone 參數(shù)需要設(shè)置為YES, 不然如果在dealloc中執(zhí)行stop函數(shù) waitUntilDone設(shè)置為NO的話會進行異步操作導(dǎo)致野指針
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}
-(void)stopThread{
    isStop = false;
    CFRunLoopStop(CFRunLoopGetCurrent())
}

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

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

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