為什么子線程RunLoop需要手動開啟

RunLoop是iOS中處理循環(huán)事件以及管理和處理消息的對象冒晰,通過在runloop中注冊不同的觀察者對象和回調(diào)處理來處理source0和source1事件览绿。通過簡單的的示例我們來觀察一下為什么子線程RunLoop需要手動開啟轨域。

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"1");
        [self performSelector:@selector(test) withObject:nil afterDelay:0];
        NSLog(@"2");
    });
}
-(void)test{
    NSLog(@"3");
}

我們運(yùn)行一下可看到如下打印效果 :
1 2
如果我們在子線程中獲取該線程中的runloop祷舀,我們在看下打印結(jié)果

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"1");
        [self performSelector:@selector(test) withObject:nil afterDelay:0];
        [[NSRunLoop currentRunLoop] run];
        NSLog(@"2");
    });
    
}
-(void)test{
    NSLog(@"3");
}

輸出結(jié)果如下:
1 3 2
如同我們所知道的那樣鉴未,performSelector:withObject:afterDelay:是在當(dāng)前runloop中注冊了一個Timer事件舶吗,但是當(dāng)前線程并沒有處理該Timer事件,結(jié)合runloop源碼我們探索一下

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

 _CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
_CFUnlock(&loopsLock);
    // __CFRunLoops是個CFMutableDictionaryRef類型的全局變量洒疚,用來保存RunLoop歹颓。這里首先判斷有沒有這個全局變量,如果沒有就新創(chuàng)建一個這個全局變量油湖,并同時創(chuàng)建一個主線程對應(yīng)的runloop
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
    // 創(chuàng)建一個主線程對應(yīng)的runloop巍扛。
    CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
    // 保存主線程
    CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
        CFRelease(dict);
    }
    CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    // 根據(jù)線程取其對應(yīng)的runloop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
     // 如果這個線程沒有對應(yīng)的runloop,就新建立一個runloop對象
    if (!loop) {
        //RunLoop 懶加載的方式7Φ隆3芳椤!
    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)) {
             // 注冊一個回調(diào)喊括,當(dāng)線程銷毀時一同銷毀對應(yīng)的runloop
            _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
        }
    }
    return loop;
}

當(dāng)調(diào)用 [NSRunloop currentLoop]的時候胧瓜,會將當(dāng)前線程的指針傳入_CFRunLoopGet0函數(shù)中,會將當(dāng)前線程和當(dāng)前線程對應(yīng)的runloop對象保存在一個全局的容器里面郑什,如果當(dāng)前線程沒有runloop府喳,會使用懶加載的方式創(chuàng)建一個runloop,并保存到全局的容器中蘑拯,這也就解釋了為什么子線程的runloop需要手動的獲取钝满,而不是默認(rèn)開啟的了肉津,并且每個子線程和該線程下的runloop是一一對應(yīng)的關(guān)系。
附件 Runloop 文件 https://github.com/DevaLee/Runloop.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末舱沧,一起剝皮案震驚了整個濱河市妹沙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌熟吏,老刑警劉巖距糖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異牵寺,居然都是意外死亡悍引,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門帽氓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來趣斤,“玉大人,你說我怎么就攤上這事黎休∨欤” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵势腮,是天一觀的道長联贩。 經(jīng)常有香客問我,道長捎拯,這世上最難降的妖魔是什么泪幌? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮署照,結(jié)果婚禮上祸泪,老公的妹妹穿的比我還像新娘。我一直安慰自己建芙,他們只是感情好没隘,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著岁钓,像睡著了一般升略。 火紅的嫁衣襯著肌膚如雪微王。 梳的紋絲不亂的頭發(fā)上屡限,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機(jī)與錄音炕倘,去河邊找鬼钧大。 笑死,一個胖子當(dāng)著我的面吹牛罩旋,可吹牛的內(nèi)容都是我干的啊央。 我是一名探鬼主播眶诈,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瓜饥!你這毒婦竟也來了逝撬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤乓土,失蹤者是張志新(化名)和其女友劉穎宪潮,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體趣苏,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狡相,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了食磕。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尽棕。...
    茶點(diǎn)故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖彬伦,靈堂內(nèi)的尸體忽然破棺而出滔悉,到底是詐尸還是另有隱情,我是刑警寧澤单绑,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布氧敢,位于F島的核電站,受9級特大地震影響询张,放射性物質(zhì)發(fā)生泄漏孙乖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一份氧、第九天 我趴在偏房一處隱蔽的房頂上張望唯袄。 院中可真熱鬧,春花似錦蜗帜、人聲如沸恋拷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蔬顾。三九已至,卻和暖如春湘捎,著一層夾襖步出監(jiān)牢的瞬間诀豁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工窥妇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留舷胜,地道東北人。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓活翩,卻偏偏與公主長得像烹骨,于是被迫代替她去往敵國和親翻伺。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評論 2 350