RunLoop

RunLoop 是什么

RunLoop 是一個(gè)事件處理的循環(huán)畜挨,目的是有工作要做時(shí)讓線程忙碌,沒有工作要做時(shí)讓線程進(jìn)入睡眠狀態(tài)萍启。

Cocoa 和 Core Foundation 兩個(gè)層面都提供了 RunLoop 的對(duì)象驶乾,即 NSRunLoop 和 CFRunLoop。

RunLoop 與線程是一對(duì)一的關(guān)系喊暖。不能手動(dòng)創(chuàng)建 RunLoop,只能 [NSRunLoop currentRunLoop] 或 CFRunLoopGetCurrent()撕瞧,調(diào)用這兩個(gè)方法時(shí)當(dāng)沒有 RunLoop 會(huì)自動(dòng)創(chuàng)建一個(gè)并返回陵叽。

模式

我們常用到的三種模式:

  • NSDefaultRunLoopMode (kCFRunLoopDefaultMode):默認(rèn)模式。
  • UITrackingRunLoopMode:當(dāng)拖動(dòng) UIScrollView 觸發(fā)的模式丛版。
  • NSRunLoopCommonModes (kCFRunLoopCommonModes) 占位模式巩掺,相當(dāng)于前面兩種模式都添加進(jìn)去了。

一個(gè) Runloop 可以有多個(gè)模式, Runloop 執(zhí)行其中一種模式的時(shí)候页畦,另外的模式不會(huì)執(zhí)行胖替。

兩種 Sources

Input Sources 和 Timer Sources。

Sources 的作用是使 RunLoop 從睡眠狀態(tài)轉(zhuǎn)化到工作狀態(tài)豫缨。

Input Sources 是指 Port-Based Sources独令、Custom Input Sources、Cocoa Perform Selector Sources好芭。

Port-Based Sources 的類有:

NSPort
NSMachPort
...

Custom Input Sources 通常用到 CFRunLoopSourceRef

Cocoa Perform Selector Sources 有以下的方法:

performSelectorOnMainThread:withObject:waitUntilDone:
performSelectorOnMainThread:withObject:waitUntilDone:modes:

performSelector:onThread:withObject:waitUntilDone:
performSelector:onThread:withObject:waitUntilDone:modes:

performSelector:withObject:afterDelay:
performSelector:withObject:afterDelay:inModes:  

cancelPreviousPerformRequestsWithTarget:
cancelPreviousPerformRequestsWithTarget:selector:object:

Timer Sources 就是指 NSTimer燃箭。

Observers

可以添加觀察者去觀察 RunLoop 的狀態(tài)的變化,狀態(tài)有以下幾種

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),
    kCFRunLoopBeforeTimers = (1UL << 1),
    kCFRunLoopBeforeSources = (1UL << 2),
    kCFRunLoopBeforeWaiting = (1UL << 5),
    kCFRunLoopAfterWaiting = (1UL << 6),
    kCFRunLoopExit = (1UL << 7),
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

應(yīng)用例子

子線程里創(chuàng)建一個(gè)定時(shí)執(zhí)行的跑圈

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    static int count = 0;
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"執(zhí)行定時(shí)器任務(wù)");
        count ++;
        if (count >= 5) {
            [timer invalidate];
        }
    }];
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addTimer:timer forMode:NSRunLoopCommonModes];
    NSLog(@"runLoop 開啟");
    [runLoop run];
    NSLog(@"runLoop 退出");
});

運(yùn)行結(jié)果:

runLoop 開啟
執(zhí)行定時(shí)器任務(wù)
執(zhí)行定時(shí)器任務(wù)
執(zhí)行定時(shí)器任務(wù)
執(zhí)行定時(shí)器任務(wù)
執(zhí)行定時(shí)器任務(wù)
runLoop 退出

注意當(dāng) Timer 不失效舍败,那么 RunLoop 永遠(yuǎn)不會(huì)退出招狸。

主線程中使用 [NSTimer scheduledTimerWith...] 并不需要 RunLoop 去 addTimer 是因?yàn)榇朔椒J(rèn)在主線程的 RunLoop 添加該 Timer。

子線程里創(chuàng)建一個(gè)監(jiān)聽 port 的跑圈

- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        self.port = [NSPort port];
        self.port.delegate = self;

        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:self.port forMode:NSDefaultRunLoopMode];

        NSLog(@"開始");
        [runLoop run];
        NSLog(@"結(jié)束");
    });
}

- (void)handlePortMessage:(NSPortMessage *)message {
    NSLog(@"handlePortMessage:%@", message);
    [[NSRunLoop currentRunLoop] removePort:self.port forMode:NSDefaultRunLoopMode];
}

- (IBAction)clickSendButton:(id)sender {
    [self.port sendBeforeDate:[NSDate date] components:nil from:nil reserved:0];
}

移除 Port 即可退出 RunLoop邻薯。

解決 Timer 添加到主線程時(shí)的問題

因?yàn)?Timer 默認(rèn)是以 NSDefaultRunLoopMode 添加到 RunLoop 的裙戏,當(dāng)拖動(dòng) UIScrollView 時(shí),切換到了 UITrackingRunLoopMode 厕诡,所以會(huì)導(dǎo)致Timer 的任務(wù)被延遲執(zhí)行了累榜,解決這個(gè)問題可以把 Timer 添加到 NSRunLoopCommonModes 模式。但是可能會(huì)引起另外一個(gè)問題灵嫌,如果 Timer 中的任務(wù)比較耗時(shí)信柿,會(huì)影響拖動(dòng) UIScrollView 的效果冀偶,這時(shí)可以把 Timer 在子線程中運(yùn)行。

把加載多張圖片的耗時(shí)操作分散到多次循環(huán)中

之前發(fā)現(xiàn)一個(gè)利用 RunLoop 優(yōu)化的開源代碼 RunLoopWorkDistribution渔嚷,運(yùn)行效果自行查看開源項(xiàng)目 https://github.com/diwu/RunLoopWorkDistribution

沒優(yōu)化前的主要問題是:多張圖片的加載任務(wù)會(huì)是需要在一次循環(huán)中執(zhí)行完的稠曼,所以會(huì)比較耗時(shí)形病,造成界面的卡頓。解決技巧是把每一張圖片分散在每一次循環(huán)中霞幅,用一個(gè)任務(wù)隊(duì)列依次加載圖片漠吻。為了觸發(fā)多次循環(huán),添加一個(gè) Timer 去觸發(fā)司恳。

檢測卡頓

添加一個(gè)觀察者途乃,觀察 RunLoop 的 處理事件前的狀態(tài)和睡眠前的狀態(tài),兩者之間的時(shí)間差達(dá)到一定值扔傅,并且連續(xù)五次以上耍共,就判定為卡頓。
參考開源代碼 https://github.com/suifengqjn/PerformanceMonitor

處理一些交互畫面問題

有時(shí)會(huì)發(fā)生一些奇怪的交互畫面問題猎塞,原因是兩個(gè) UI 更新都在 RunLoop 的同一個(gè)循環(huán)里了试读,解決方法是把其中一個(gè)放到下一個(gè)循環(huán)當(dāng)中。最便捷的就是調(diào)用 performSelector:withObject:afterDelay:

還有其他應(yīng)用例子待添加荠耽。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钩骇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子铝量,更是在濱河造成了極大的恐慌倘屹,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慢叨,死亡現(xiàn)場離奇詭異纽匙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)插爹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門哄辣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人赠尾,你說我怎么就攤上這事力穗。” “怎么了气嫁?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵当窗,是天一觀的道長。 經(jīng)常有香客問我寸宵,道長崖面,這世上最難降的妖魔是什么元咙? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮巫员,結(jié)果婚禮上庶香,老公的妹妹穿的比我還像新娘。我一直安慰自己简识,他們只是感情好赶掖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著七扰,像睡著了一般奢赂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上颈走,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天膳灶,我揣著相機(jī)與錄音,去河邊找鬼立由。 笑死轧钓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拆吆。 我是一名探鬼主播聋迎,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼枣耀!你這毒婦竟也來了霉晕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤捞奕,失蹤者是張志新(化名)和其女友劉穎牺堰,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颅围,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伟葫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了院促。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筏养。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖常拓,靈堂內(nèi)的尸體忽然破棺而出渐溶,到底是詐尸還是另有隱情,我是刑警寧澤弄抬,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布茎辐,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拖陆。R本人自食惡果不足惜弛槐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望依啰。 院中可真熱鬧乎串,春花似錦、人聲如沸速警。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坏瞄。三九已至,卻和暖如春甩卓,著一層夾襖步出監(jiān)牢的瞬間鸠匀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來泰國打工逾柿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缀棍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓机错,卻偏偏與公主長得像爬范,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子弱匪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354