模擬主線程RunLoop的mode的切換

對RunLoop運行機制不熟悉的可以先看我的這篇文章:深入理解RunLoop

我們都知道酸舍,iOS的tableView能做到滑動很平滑,一部分是依賴于runloop的mode的切換。當系統(tǒng)檢測到有scrollerview滑動時饮睬,系統(tǒng)就會將當前進程的主線程切換到UITrackingRunLoopMode,直到滑動結(jié)束现恼,又會切換到NSDefaultRunLoopMode。這個過程聽起來很奇妙量九,那么他是怎么做到的呢,我們能不能在需要的時候也這么做呢颂碧?

答案是肯定的荠列,我們可以模擬這個過程,我的思路是這樣的:由于主線程不是一個runloop干凈的線程载城,所以我們另外啟動一個子線程肌似,

    //使用GCD啟動一個子線程
    dispatch_async(dispatch_get_global_queue(0, 0), ^{

    });

為了保證線程不退出,我們添加一個timer, 讓其在下NSDefaultRunLoopMode模式下運行,按正常情況下诉瓦,下面的代碼會在一直打印“timer1 fired”川队,該子線程會永遠運行在NSDefaultRunLoopMode模式下力细。

    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        //timer1運行在default mode
        NSTimer *timer1 = [NSTimer timerWithTimeInterval:1.f repeats:YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"timer1 fired");
        }];
        [[NSRunLoop currentRunLoop] addTimer:timer1 forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
    });

但是主線程是可以切換mode的,那么這樣肯定不行固额,我們只有改變runloop的啟動方式眠蚂,只有用更好控制的runMode: beforeDate:這個函數(shù),大家知道這個函數(shù)運行起來的runloop斗躏,會在指定的mode下運行一次便會退出逝慧,但是由于我們添加的是timer,所有不會主動退出啄糙,但是我們可以采用CFRunLoopStop()主動退出笛臣,只有runloop能退出,我們才又機會切換mode隧饼,下面是實現(xiàn)代碼


    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        //timer1運行在default mode
        NSTimer *timer1 = [NSTimer timerWithTimeInterval:1.f repeats:YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"timer1 fired");
        }];
        [[NSRunLoop currentRunLoop] addTimer:timer1 forMode:NSDefaultRunLoopMode];

        self.currentMode = kCFRunLoopDefaultMode;
        while (1) {
            [[NSRunLoop currentRunLoop] runMode:self.currentMode beforeDate:[NSDate distantFuture]];
        }
    });

這樣寫就可以通過控制currentMode這個全局變量來控制runloop下次運行的mode沈堡。我們模擬在touchbegan的時候切換到UITrackingRunLoopMode,touchend的時候又切換回NSDefaultRunLoopMode燕雁,為了顯示runloop確實切換到了UITrackingRunLoopMode踱蛀,我們啟動另一個timer讓其運行在UITrackingRunLoopMode,看打印日志就可以了贵白。代碼實現(xiàn)是這樣的:


    dispatch_async(dispatch_get_global_queue(0, 0), ^{

       self.rl = CFRunLoopGetCurrent();

        //timer1運行在default mode
        NSTimer *timer1 = [NSTimer timerWithTimeInterval:1.f repeats:YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"timer1 fired");
        }];
        [[NSRunLoop currentRunLoop] addTimer:timer1 forMode:NSDefaultRunLoopMode];

        //timer2運行在track Mode
        NSTimer *timer2 = [NSTimer timerWithTimeInterval:1.f repeats:YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"timer2 fired");
        }];
        [[NSRunLoop currentRunLoop] addTimer:timer2 forMode:UITrackingRunLoopMode];

        //指定當前運行mode
        self.currentMode = NSDefaultRunLoopMode;
        while (1) {
            [[NSRunLoop currentRunLoop] runMode:self.currentMode beforeDate:[NSDate distantFuture]];
        }
    });


  - (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        NSLog(@"touch began")
        //touchbegan 切換成track mode
        self.currentMode = UITrackingRunLoopMode;
        CFRunLoopStop(self.rl);
   }

   - (void) touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
      NSLog(@"touch end");
      //touchend 切換成kCFRunLoopDefaultMode
      self.currentMode = kCFRunLoopDefaultMode;
      CFRunLoopStop(self.rl);
  }

代碼運行起來后率拒,就可以看到,在觸摸之前只有timer1打印禁荒,在手指觸摸時之后timer2打印

2020-06-05 14:48:39.932264+0800 [18468:8621222] timer1 fired
2020-06-05 14:48:40.932321+0800 [18468:8621222] timer1 fired
2020-06-05 14:48:41.934566+0800 [18468:8621222] timer1 fired
2020-06-05 14:48:42.934567+0800 [18468:8621222] timer1 fired
2020-06-05 14:48:43.344774+0800 [18468:8620979] touch began
2020-06-05 14:48:43.345429+0800 [18468:8621222] timer2 fired
2020-06-05 14:48:43.934576+0800 [18468:8621222] timer2 fired
2020-06-05 14:48:44.929843+0800 [18468:8621222] timer2 fired
2020-06-05 14:48:45.294261+0800 [18468:8620979] touch end
2020-06-05 14:48:45.294846+0800 [18468:8621222] timer1 fired

通過這樣我們就模擬了系統(tǒng)的mode切換過程猬膨,也順便知道了,為什么NSTimer需要設置在NSRunLoopCommonModes模式下運行呛伴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末勃痴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子热康,更是在濱河造成了極大的恐慌沛申,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姐军,死亡現(xiàn)場離奇詭異铁材,居然都是意外死亡,警方通過查閱死者的電腦和手機奕锌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門著觉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人惊暴,你說我怎么就攤上這事饼丘。” “怎么了辽话?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵肄鸽,是天一觀的道長卫病。 經(jīng)常有香客問我,道長典徘,這世上最難降的妖魔是什么忽肛? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮烂斋,結(jié)果婚禮上屹逛,老公的妹妹穿的比我還像新娘。我一直安慰自己汛骂,他們只是感情好罕模,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著帘瞭,像睡著了一般淑掌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蝶念,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天抛腕,我揣著相機與錄音,去河邊找鬼媒殉。 笑死担敌,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的廷蓉。 我是一名探鬼主播全封,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼桃犬!你這毒婦竟也來了刹悴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤攒暇,失蹤者是張志新(化名)和其女友劉穎土匀,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體形用,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡就轧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了尾序。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钓丰。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖每币,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情琢歇,我是刑警寧澤兰怠,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布梦鉴,位于F島的核電站,受9級特大地震影響揭保,放射性物質(zhì)發(fā)生泄漏肥橙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一秸侣、第九天 我趴在偏房一處隱蔽的房頂上張望存筏。 院中可真熱鬧,春花似錦味榛、人聲如沸椭坚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽善茎。三九已至,卻和暖如春频轿,著一層夾襖步出監(jiān)牢的瞬間垂涯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工航邢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留耕赘,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓膳殷,卻偏偏與公主長得像鞠苟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子秽之,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353