iOS RunLoop簡單理解與運用

二話不說先上我麥來壓壓驚


什么是RunLoop辖佣?

RunLoop顧名思義運行著的循環(huán)霹抛,而且是一個死循環(huán),本質(zhì)是一個do,while循環(huán)卷谈,他負責監(jiān)聽幾乎所有的事件(觸摸事件杯拐,網(wǎng)絡事件,時鐘事件)世蔗,當監(jiān)聽到了事件就會喚醒線程去執(zhí)行端逼,沒事件RunLoop的線程就休眠。

RunLoop常見的模式

RunLoop在同一時間中只能響應一種模式下的事件污淋,同時會關閉上一種模式顶滩;不同的事件都有對應的模式,處理事件都會切換到事件對應的模式寸爆。
<ul>
<li> NSDefalutRunLoopMode //默認模式
<li> UITrackingRunLoopMode //拖動事件诲祸,例如:UIScrollView
<li> NSRunLoopCommonModes //占位模式,包含前面兩個

RunLoop跟線程的關系

  • RunLoop跟線程是一一對應的而昨,不能主動創(chuàng)建救氯,只能通過獲取來拿到RunLoop對象

  • 在主線程中RunLoop是默認創(chuàng)建開啟的,來保證線程不死歌憨,程序不退出着憨;在分線程中,默認沒有創(chuàng)建RunLoop务嫡,線程在執(zhí)行完任務之后就會退出甲抖,當我們在線程中獲取RunLoop時漆改,它才會創(chuàng)建,一旦run起來准谚,這個分線程將跟主線程一樣不會主動退出挫剑。

      開啟一個線程,并創(chuàng)建一個定時器
      
      NSThread *thread = [[NSThread alloc]initWithBlock:^{
         NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
              NSLog(@"哈哈");
          }];
          [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
          //1.[[NSRunLoop currentRunLoop] run];
          //2.while (!_isFinished) {
          //  [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.001]];
          //}
          NSLog(@"你看,來了嗎柱衔?");
       }];
      [thread start];
    

    來看看運行結(jié)果:

    發(fā)現(xiàn)定時器并沒有執(zhí)行回調(diào)方法!

    原因:子線程中默認是不開啟RunLoop的樊破,所以線程里面代碼執(zhí)行完之后,線程就銷毀了唆铐。

    解決:讓線程常駐 -> 開啟RunLoop哲戚。

    1. 直接run -> [[NSRunLoop currentRunLoop] run];但是在執(zhí)行run方法之后,在當前線程中后面的代碼將永遠不會再執(zhí)行艾岂,比如NSLog(@"你看顺少,來了嗎?");王浴,因為RunLoop會一直在那里循環(huán)脆炎,所以這個線程將永遠不會再自動銷毀,只能通過執(zhí)行[NSThread exit]來暴力退出氓辣,RunLoop也會隨之關閉秒裕,當然線程中后續(xù)代碼更加不會再執(zhí)行。

    2. 手動添加一個循環(huán)來控制RunLoop循環(huán)筛婉,runUntilDate:同run方法開啟RunLoop簇爆,只不過是加了一個時間限制癞松,在未來多少秒暫停爽撒,這樣通過手動控制RunLoop循環(huán)就可以控制線程的生命周期,當關閉此循環(huán)响蓉,Runloop也會停止硕勿,然后會執(zhí)行線程剩余代碼,之后線程銷毀枫甲,這樣做的好處就是可控制源武。

      while (!_isFinished) {
          
             [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.001]];
          }
      

RunLoop優(yōu)化

RunLoop如何優(yōu)化?我們都知道耗時操作放到子線程中執(zhí)行想幻,更新UI放到主線程執(zhí)行(在子線程中更新UI也會等到子線程所有操作執(zhí)行完了然后再到主線程中執(zhí)行UI操作粱栖,因為UIKit是線程不安全的:UI控件都是用的原子屬性->效率高。)脏毯,那么如果更新UI耗時長怎么辦闹究?其實是因為RunLoop在一次循環(huán)當中執(zhí)行了過多的復雜的UI操作,我們需要對UI操作做拆分食店,然后增加RunLoop的循環(huán)次數(shù)來分步執(zhí)行渣淤,一次循環(huán)只做一個UI更新模塊赏寇,從而保證RunLoop的流暢性。

- (void)addRunLoopObserver{
    
    //添加RunLoop監(jiān)聽,需要使用CFRunLoop价认,因為NSRunloop沒有這個功能
    
    //1.獲取runloop
    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
    
    //2.1創(chuàng)建上下文
    CFRunLoopObserverContext context = {
        
        0, //這個context的版本
        (__bridge void *)(self), //傳入的參數(shù)嗅定,這里我傳入控制器
        CFRetain, //告訴它retain是調(diào)用哪個函數(shù)
        CFRelease, //告訴它release是調(diào)用哪個函數(shù)
        nil,
    };
    
    //2.2創(chuàng)建runloop觀察者
    /*
     CFRunLoopObserverRef CFRunLoopObserverCreate(CFAllocatorRef allocator,
                                                 CFOptionFlags activities,
                                                 Boolean repeats,
                                                 CFIndex order,
                                                 CFRunLoopObserverCallBack callout,
                                                 CFRunLoopObserverContext *context);
     
     @param allocator:這個參數(shù)用來分配空間給新的對象。默認情況下使用NULL或者kCFAllocatorDefault用踩。
     @param activities:設置Runloop的運行階段的標志渠退,當運行到此階段時,CFRunLoopObserver會被調(diào)用
     @param repeats:CFRunLoopObserver是否循環(huán)調(diào)用捶箱,false為單詞調(diào)用智什,否則循環(huán)調(diào)用。
     @param order:CFRunLoopObserver的優(yōu)先級丁屎,當在Runloop同一運行階段中有多個CFRunLoopObserver時荠锭,根據(jù)這個來先后調(diào)用CFRunLoopObserver。正常情況下使用0晨川。
     @param callout:回調(diào)函數(shù)
     @param context:CFRunLoopObserver結(jié)構(gòu)體里面的一個結(jié)構(gòu)體证九,它主要用來給回調(diào)函數(shù)傳遞消息的。
     @return CFRunLoopObserverRef:觀察者指針對象
     */
    CFRunLoopObserverRef observer = CFRunLoopObserverCreate(NULL,
                                                            kCFRunLoopBeforeWaiting,
                                                            YES, 0,
                                                            callBack,
                                                            &context);
    
    //3.給runloop添加觀察者
    CFRunLoopAddObserver(runLoop, observer, kCFRunLoopCommonModes);
    
    
    //4.通過定時器來增加runloop循環(huán)次數(shù)
    [NSTimer scheduledTimerWithTimeInterval:0.001
                                     target:self
                                   selector:@selector(timerAction)
                                   userInfo:nil
                                    repeats:YES];
}

- (void)timerAction{
    //不執(zhí)行任何代碼共虑,只作為一個觸發(fā)runloop監(jiān)聽回調(diào)的功能愧怜。
}

/**
 回調(diào)函數(shù),在kCFRunLoopBeforeWaiting(runloop等待執(zhí)行循環(huán)之前)情況下調(diào)用,因為定時器給的是0.001秒妈拌,所以這里調(diào)用非常頻繁拥坛,保證一次循環(huán)執(zhí)行一個任務

 @param observer 觀察者對象
 @param activity Runloop的運行階段的標志
 @param info CFRunLoopObserverContext傳入的參數(shù)
 */
void callBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
    
    //1.拿到傳過來的參數(shù),再進行轉(zhuǎn)換,因為這是C函數(shù)尘分,不能直接調(diào)用self猜惋,所以在添加觀察的時候把self傳過來。
    ViewController *vc = (__bridge ViewController *)(info);
    
    //2.執(zhí)行拆分之后的UI模塊(拆分的模塊用task數(shù)組裝起來培愁,具體任務包裝在block中著摔,在這里只要執(zhí)行block即可)
    if (vc.tasks.count == 0) {
        return;
    }
    RunLoopBlock block = vc.tasks.firstObject;
    !block?:block();
    [vc.tasks removeObjectAtIndex:0];
}
    
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市定续,隨后出現(xiàn)的幾起案子谍咆,更是在濱河造成了極大的恐慌,老刑警劉巖私股,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摹察,死亡現(xiàn)場離奇詭異,居然都是意外死亡倡鲸,警方通過查閱死者的電腦和手機供嚎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人查坪,你說我怎么就攤上這事寸宏。” “怎么了偿曙?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵氮凝,是天一觀的道長。 經(jīng)常有香客問我望忆,道長罩阵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任启摄,我火速辦了婚禮稿壁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘歉备。我一直安慰自己傅是,他們只是感情好,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布蕾羊。 她就那樣靜靜地躺著喧笔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪龟再。 梳的紋絲不亂的頭發(fā)上书闸,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音利凑,去河邊找鬼浆劲。 笑死,一個胖子當著我的面吹牛哀澈,可吹牛的內(nèi)容都是我干的牌借。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼日丹,長吁一口氣:“原來是場噩夢啊……” “哼走哺!你這毒婦竟也來了蚯嫌?” 一聲冷哼從身側(cè)響起哲虾,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎择示,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體栅盲,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡汪诉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扒寄。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鱼鼓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出该编,到底是詐尸還是另有隱情迄本,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布课竣,位于F島的核電站嘉赎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏于樟。R本人自食惡果不足惜公条,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望迂曲。 院中可真熱鬧靶橱,春花似錦、人聲如沸路捧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鬓长。三九已至谒拴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涉波,已是汗流浹背英上。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留啤覆,地道東北人苍日。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像窗声,于是被迫代替她去往敵國和親相恃。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

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