詳解Run Loop

Run Loop

  • Run Loop是事件驅(qū)動(dòng)的。
  • iOS中有2套API來訪問使用Run Loop
    • Foundation(OC語言)
      • NSRun Loop(對CFRun LoopREF的封裝)
    • Core Foundation(C語言)
      • CFRun LoopREF

Run Loop與線程

  • 每條線程都有唯一的Run Loop對象與之對應(yīng)懂算。
  • 主線程的Run Loop會自動(dòng)創(chuàng)建,子線程的Run Loop需要手動(dòng)創(chuàng)建穗慕。
  • Run Loop在第一次獲取時(shí)創(chuàng)建,在線程結(jié)束時(shí)銷毀妻导。

獲取Run Loop對象

  • 獲取當(dāng)前線程的Run Loop對象

    //Foundation
    [NSRun Loop currentRun Loop];
    //Core Foundation
    CFRun LoopGetCurrent();
    
  • 獲取主線程Run Loop

//Foundation
[NSRun Loop mainRun Loop];
//Core Foundation
CFRun LoopGetMain();

Run Loop相關(guān)類

CFRun LoopRef
CFRun LoopModeRef
CFRun LoopSourceRef
CFRun LoopTimerRef
CFRun LoopObserverRef


RunLoop.png
  • 一個(gè) Run Loop 包含若干個(gè)Mode逛绵,每個(gè)Mode中包含若干個(gè)Source/Oberver/Timer。

CFRun LoopModeRef

  • CFRun LoopModelRef代表 Run Loop 的運(yùn)行模式

  • 每次Run Loop啟動(dòng)的時(shí)候倔韭,只能指定一個(gè)Mode术浪。這個(gè)Mode叫做CurrentMode。

    [[NSRun Loop currentRun Loop] runMode:<(nonnull NSString *)> beforeDate:<(nonnull NSDate *)>];
    
  • 如果想要切換Mode寿酌,只能退出Loop胰苏,再進(jìn)入新的Mode。這樣做的目的是分隔開不同組的Source/Oberver/Timer醇疼,讓其互不影響硕并。

  • 系統(tǒng)默認(rèn)注冊的5個(gè)Mode:

    • kCFRun LoopDefaultMode:App的默認(rèn)Mode法焰,通常主線程是在這個(gè)Mode下運(yùn)行。
    • UITrackingRun LoopMode:界面跟蹤 Mode倔毙,用于 ScrollView 追蹤觸摸滑動(dòng)埃仪,保證界面滑動(dòng)時(shí)不受其他 Mode 影響。
    • UIInitializationRun LoopMode: 在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode陕赃,啟動(dòng)完成后就不再使用卵蛉。
    • GSEventReceiveRun LoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到么库。
    • kCFRun LoopCommonModes: 這是一個(gè)占位用的Mode傻丝,不是一種真正的Mode。

CFRun LoopTimerRef

  • CFRun LoopTimerRef是基于時(shí)間的觸發(fā)器

  • 舉例:

    //此方法是通過Run Loop實(shí)現(xiàn)的诉儒,Mode為:kCFRun LoopDefaultMode
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    
    //等價(jià)于
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    [[NSRun Loop currentRun Loop] addTimer:timer forMode:NSDefaultRun LoopMode];
    

CFRun LoopSourceRef

  • CFRun LoopSourceRef是事件源葡缰。(輸入源)
  • Source的分類
    • Source0:非基于Port的。(舉例:按鈕點(diǎn)擊事件)


      監(jiān)聽按鈕點(diǎn)擊的函數(shù)棧
    • Source1:基于Port的允睹,通過內(nèi)核和其他線程通信运准、接收幌氮、分發(fā)系統(tǒng)事件缭受。(舉例:手指點(diǎn)擊屏幕的時(shí)候,先把事件包裝成一個(gè)Event该互,先到Source1米者,之后分發(fā)個(gè)Source0)

CFRun LoopObserverRef

  • CFRun LoopObserverRef是觀察者,可以監(jiān)聽Run Loop的狀態(tài)改變宇智。

  • 能夠監(jiān)聽的時(shí)間點(diǎn):

    /* Run Loop Observer Activities */
    typedef CF_OPTIONS(CFOptionFlags, CFRun LoopActivity) {
        kCFRun LoopEntry = (1UL << 0), //即將進(jìn)入Run Loop
        kCFRun LoopBeforeTimers = (1UL << 1), //即將處理Timer
        kCFRun LoopBeforeSources = (1UL << 2), //即將處理Source
        kCFRun LoopBeforeWaiting = (1UL << 5), //即將進(jìn)入休眠
        kCFRun LoopAfterWaiting = (1UL << 6), //剛從休眠中喚醒
        kCFRun LoopExit = (1UL << 7), //即將退出Run Loop
        kCFRun LoopAllActivities = 0x0FFFFFFFU
    };
    
  • 舉例實(shí)現(xiàn):

      //沒有NS封裝過的函數(shù)蔓搞,只有這一種方式。
      // 創(chuàng)建observer随橘,通過block創(chuàng)建.也可通過函數(shù)回調(diào)創(chuàng)建:FRunLoopObserverRefCFRunLoopObserverCreate(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, CFRun LoopObserverCallBack callout, CFRun LoopObserverContext *context);
      CFRun LoopObserverRef observer = CFRun LoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRun LoopAllActivities, YES, 0, ^(CFRun LoopObserverRef observer, CFRun LoopActivity activity) {
        NSLog(@"----監(jiān)聽到Run Loop狀態(tài)發(fā)生改變---%zd", activity);
      });
      //CFRun LoopObserverRef CFRun LoopObserverCreateWithHandler(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, void (^block) (CFRun LoopObserverRef observer, CFRun LoopActivity activity));
      //第一個(gè)參數(shù):CFAllocatorGetDefault();第二個(gè)參數(shù):能監(jiān)聽的狀態(tài);第三個(gè)參數(shù):是否重復(fù);第四個(gè)參數(shù):傳0;
      // 添加觀察者:監(jiān)聽Run Loop的狀態(tài)
      CFRun LoopAddObserver(CFRun LoopGetCurrent(), observer, kCFRun LoopDefaultMode);
      //void CFRun LoopAddObserver(CFRun LoopRef rl, CFRun LoopObserverRef observer, CFStringRef mode);
      //第一個(gè)參數(shù):需要監(jiān)聽的Run Loop;第二個(gè)參數(shù):觀察者;第三個(gè)參數(shù):Run Loop的模式;
    // 釋放Observer
    CFRelease(observer);
    
    • 注意:Core Foundation(CF)的內(nèi)存管理喂分,凡是帶有Create、Copy机蔗、Retain等字眼的函數(shù)蒲祈,創(chuàng)建出來的對象,都需要在最后做一次release萝嘁。ARC是Foundation里的梆掸。

Run Loop處理邏輯:

Run Loop
  • Run Loop的事件隊(duì)列:
    • 每次運(yùn)行Run Loop,你線程的Run Loop對會自動(dòng)處理之前未處理的消息牙言,并通知相關(guān)的觀察者酸钦。具體的順序如下:
      1. 通知觀察者Run Loop已經(jīng)啟動(dòng)。
      2. 通知觀察者任何即將要開始的定時(shí)器咱枉。
      3. 通知觀察者任何即將啟動(dòng)的非基于端口的源卑硫。
      4. 啟動(dòng)任何準(zhǔn)備好的非基于端口的源徒恋。
      5. 如果基于端口的源準(zhǔn)備好并處于等待狀態(tài),立即啟動(dòng)拔恰;并進(jìn)入步驟9因谎。
      6. 通知觀察者線程進(jìn)入休眠。
      7. 將線程置于休眠直到任一下面的事件發(fā)生:
        • 某一事件到達(dá)基于端口的源颜懊。
        • 定時(shí)器啟動(dòng)财岔。
        • Run Loop設(shè)置的時(shí)間已經(jīng)超時(shí)。
        • Run Loopp被顯式喚醒河爹。
      8. 通知觀察者線程將被喚醒匠璧。
      9. 處理未處理的事件。
        • 如果用戶定義的定時(shí)器啟動(dòng)咸这,處理定時(shí)器事件并重啟Run Loop夷恍。進(jìn)入步驟2。
        • 如果輸入源啟動(dòng)媳维,傳遞相應(yīng)的消息酿雪。
        • 如果Run Loop被顯式喚醒而且時(shí)間還沒超時(shí),重啟Run Loop侄刽。進(jìn)入步驟2指黎。
      10. 通知觀察者Run Loop結(jié)束。

Run Loop應(yīng)用

  1. 啟動(dòng)Run Loop

    [[NSRunLoop currentRunLoop] run];
    //等于
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    //等于
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
    
  2. 只在NSDefaultRunLoopMode模式下顯示圖片

    [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"placeholder"] afterDelay:3.0 inModes:@[NSDefaultRunLoopMode]];
    
  3. 常駐線程州丹,保證線程不死

    [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
    
    //或者(不推薦)
    while(flag)
    {
        [[NSRunLoop currentRunLoop] run];
    }
    
  4. 子線程定時(shí)做事情(NSTimer)

    NSTimer *time = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(<selector>) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:time forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
    //或者
    NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(<selector>) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] run];
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末醋安,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子墓毒,更是在濱河造成了極大的恐慌吓揪,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件所计,死亡現(xiàn)場離奇詭異柠辞,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)主胧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門叭首,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人讥裤,你說我怎么就攤上這事放棒。” “怎么了己英?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵间螟,是天一觀的道長。 經(jīng)常有香客問我,道長厢破,這世上最難降的妖魔是什么荣瑟? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮摩泪,結(jié)果婚禮上笆焰,老公的妹妹穿的比我還像新娘。我一直安慰自己见坑,他們只是感情好嚷掠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著荞驴,像睡著了一般不皆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上熊楼,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天霹娄,我揣著相機(jī)與錄音,去河邊找鬼鲫骗。 笑死犬耻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的执泰。 我是一名探鬼主播枕磁,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼坦胶!你這毒婦竟也來了透典?” 一聲冷哼從身側(cè)響起晴楔,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤顿苇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后税弃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纪岁,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年则果,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了幔翰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡西壮,死狀恐怖遗增,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情款青,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站勘天,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蔗坯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一燎含、第九天 我趴在偏房一處隱蔽的房頂上張望宾濒。 院中可真熱鬧,春花似錦屏箍、人聲如沸绘梦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谚咬。三九已至,卻和暖如春尚粘,著一層夾襖步出監(jiān)牢的瞬間择卦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工郎嫁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秉继,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓泽铛,卻偏偏與公主長得像尚辑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子盔腔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Run loop 剖析:Runloop 接收的輸入事件來自兩種不同的源:輸入源(intput source)和定時(shí)...
    Mitchell閱讀 12,430評論 17 111
  • 什么情況下使用runloop杠茬? runloop好比就是跑圈,就是一個(gè)線程一直在做某一件事情弛随。 一般主線程會自動(dòng)運(yùn)行...
    進(jìn)擊的小杰閱讀 4,411評論 4 7
  • 首先看一段AF2.x經(jīng)典代碼: 首先我們要明確一個(gè)概念,線程一般都是一次執(zhí)行完任務(wù)愕够,就銷毀了走贪。 而添加了runlo...
    涂耀輝閱讀 22,496評論 42 293
  • 基本概念 進(jìn)程 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,而且每個(gè)進(jìn)程之間是獨(dú)立的惑芭,它們都運(yùn)行在其專用且受保護(hù)的內(nèi)存...
    小楓123閱讀 891評論 0 1
  • 我生長在汕頭的一個(gè)鎮(zhèn)上坠狡。自從外出求學(xué)后,已經(jīng)多年沒能在家過元宵節(jié)遂跟,然而腦海中依舊保留著家鄉(xiāng)熱鬧非凡的元宵節(jié)記憶片段...
    燕子的留聲館閱讀 599評論 0 4