iOS-runloop知識(shí)點(diǎn)整理

RunLoop.png

Runloop

通過內(nèi)部維護(hù)事件循環(huán)來(lái)對(duì)事件/消息進(jìn)行管理的一個(gè)對(duì)象。 沒有消息處理時(shí),進(jìn)入休眠以避免資源占用 有消息時(shí),立刻被喚醒


904629-1c7712439e0c4135.png

事件循環(huán)(Event Loop) 事件循環(huán)對(duì)消息進(jìn)行管理,管理狀態(tài)的切換 沒有消息時(shí): 用戶態(tài) ----> 內(nèi)核態(tài) 有消息時(shí):用戶態(tài) <---- 內(nèi)核態(tài)

main函數(shù)為什么不會(huì)退出? 因?yàn)檎{(diào)用UIApplicationMain啟動(dòng)了一個(gè)Runloop。有接收消息進(jìn)行處理,沒有消息進(jìn)入等待。

NSRunLoop是CFRunLoop的封裝,提供面向?qū)ο蟮腁PI。

CFRunLoop分析

查看蘋果源碼,CFRunLoop結(jié)構(gòu)體如下:

1363078-0e84d5a6c8cc772f.png
  • pthread : 與線程相關(guān),一一對(duì)應(yīng)。

  • currentMode:當(dāng)前RunLoop所處的模式

  • modes 集合CFMutableSetRef

  • commonModes: CFMutableSetRef

  • commonModeItems: 集合,包含多個(gè)Observer、Timer、Source

CFRunLoopMode

Mode代表RunLoop的不同運(yùn)行模式,運(yùn)行時(shí)只能選擇一種模式運(yùn)行作為currentMode,如果想切換需要退出當(dāng)前循環(huán),重新選擇Mode在進(jìn)入循環(huán)。為什么這樣設(shè)計(jì)呢,為了隔離開不同的任務(wù),互不影響。

CFRunLoopMode結(jié)構(gòu)體:

1363078-0cff3ff96182087a.png

CFRunLoopModeRef

RunLoopMode中包含的內(nèi)容:

  • name: 名稱NSDefaultRunLoopMode

  • source0:處理點(diǎn)擊事件,performSelector:onThread:withObject:方法。

  • source1

  • observers 數(shù)組

  • timers 數(shù)組

一個(gè)Mode對(duì)應(yīng)多個(gè)Source/Timer/ObserverRunLoop只能接受到當(dāng)前Mode中添加的事件。

兩種常用的Mode:

  • kCFRunLoopDefaultMode(NSDefaultRunLoopMode): APP的默認(rèn)Mode,通常主線程是這個(gè)模式。

  • UITrackingRunLoopMode: 界面跟蹤Mode,用戶ScrollView追蹤觸摸滑動(dòng),保證界面滑動(dòng)是不受其他Mode的影響。

注意:NSRunLoopCommonMode不是實(shí)際存在的Mode。是同步Source/Timer/Observer到多個(gè)Mode中的一種技術(shù)方案。

CFRunLoopSource

  • source0: 手動(dòng)喚醒線程。 觸摸事件,performSelector:onThread:withObject:方法。

  • source1 :自動(dòng)喚醒線程,基于Port的線程間通訊 ,系統(tǒng)事件的捕捉。

CFRunLoopTimer

基于事件的定時(shí)器,可以與NSTimer進(jìn)行轉(zhuǎn)換。 performSelector:withObject:afterDelay:

CFRunLoopObserver

 CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
      switch (activity) {
          case kCFRunLoopEntry:
              NSLog(@"KCFRunLoopEntry");
              break;
          case kCFRunLoopBeforeTimers:
              NSLog(@"Timers");
              break;
          case kCFRunLoopBeforeSources:
              NSLog(@"Sources");
              break;
          case kCFRunLoopBeforeWaiting:
              NSLog(@"BeforeWaiting");
              break;
          case kCFRunLoopAfterWaiting:
              NSLog(@"AfterWaiting");
              break;
          case kCFRunLoopExit:
              NSLog(@"Exit");
              break;
          default:
              break;
      }
  });
  CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
  CFRelease(observer);

監(jiān)聽RunLoop狀態(tài),觀測(cè)時(shí)間點(diǎn):

  • kCFRunLoopEntry 進(jìn)入RunLoop

  • kCFRunLoopBeforeTimers 將要處理Timer事件 *kCFRunLoopBeforeSources將要處理Source事件

  • kCFRunLoopBeforeWaiting 將要進(jìn)入休眠狀態(tài),切換用戶態(tài)--->內(nèi)核態(tài)。

  • kCFRunLoopAfterWaiting 喚醒,內(nèi)核態(tài)--->用戶態(tài)

  • kCFRunLoopExit 退出

自動(dòng)釋放池跟RunLoop的狀態(tài)有關(guān)

一個(gè)RunLoop對(duì)應(yīng)多個(gè)Mode,一個(gè)Mode對(duì)應(yīng)多個(gè)Source/Timer/Observer。

事件循環(huán)的實(shí)現(xiàn)機(jī)制

1363078-13acd59cc19e6a0c.png

RunLoop的應(yīng)用

滑動(dòng)TableView的時(shí)候定時(shí)器為什么會(huì)失效?

因?yàn)榛瑒?dòng)TableView的時(shí)候,RunLoop發(fā)生了Mode切換。 kCFRunLoopDefaultMode ---> UITrackingRunLoopMode。 解決方法:將Timer添加到RunLoop使用commonMode標(biāo)記設(shè)置。

 // 這種方式添加定時(shí)器不會(huì)出現(xiàn),Timer停止的問題。
  static int count = 0;
  NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
      NSLog(@"%d", count ++);
  }];
  [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
  // NSRunLoopCommonModes 并不是一個(gè)真的模式,而是一個(gè)標(biāo)記。

如何實(shí)現(xiàn)常駐線程?

經(jīng)常使用子線程時(shí),可以創(chuàng)建一個(gè)常駐子線程,可以節(jié)省系統(tǒng)資源。下面看創(chuàng)建常駐子線程:

BOOL shouldKeepRunning = YES; // global
- (void)viewDidLoad {
  [super viewDidLoad];
  // LEThread繼承自NSThread重寫dealloc方法,打印銷毀信息。
  self.thread = [[LEThread alloc] initWithBlock:^{
      // 用于線程?;?,不做具體的任務(wù)。
      // 線程一直在運(yùn)行
      [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
//       [[NSRunLoop currentRunLoop] run]; // 開啟一個(gè)無(wú)限的循環(huán),無(wú)法停止。
      NSRunLoop *theRL = [NSRunLoop currentRunLoop];
      while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
      NSLog(@"%s ---end----", __func__);
  }];
  // 啟動(dòng)子線程
  [self.thread start];
}
?
//停止線程
// 停止子線程RunLoop,該方法需要在子線程中調(diào)用。
- (void)stop {
  shouldKeepRunning = NO; // 標(biāo)志
  CFRunLoopStop(CFRunLoopGetCurrent()); // 釋放當(dāng)前RunLoop。
  NSLog(@"%s", __func__);
}
  1. 為當(dāng)前線程開啟一個(gè)RunLoop。

  2. 向該RunLoop中添加一個(gè)Port/Source等維持RunLoop的事件循環(huán)。

  3. 啟動(dòng)該RunLoop。

小結(jié)

  • 講講RunLoop,項(xiàng)目中有用到嗎?

  • runloop內(nèi)部實(shí)現(xiàn)邏輯?

  • runloop和線程的關(guān)系?

  • timer 與 runloop 的關(guān)系?

  • 程序中添加每3秒響應(yīng)一次的NSTimer,當(dāng)拖動(dòng)tableview時(shí)timer可能無(wú)法響應(yīng)要怎么解決?

  • runloop 是怎么響應(yīng)用戶操作的, 具體流程是什么樣的?

  • 說說runLoop的幾種狀態(tài)

  • runloop的mode作用是什么?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Runloop是iOS和OSX開發(fā)中非?;A(chǔ)的一個(gè)概念,從概念開始學(xué)習(xí)。 RunLoop的概念 -般說,一個(gè)線程一...
    小貓仔閱讀 1,111評(píng)論 0 1
  • 轉(zhuǎn)載:http://www.cocoachina.com/ios/20150601/11970.html RunL...
    Gatling閱讀 1,558評(píng)論 0 13
  • 前言 RunLoop是iOS和OSX開發(fā)中非?;A(chǔ)的一個(gè)概念,這篇文章將從CFRunLoop的源碼入手,介紹Run...
    暮年古稀ZC閱讀 2,411評(píng)論 1 19
  • 轉(zhuǎn)自bireme,原地址:https://blog.ibireme.com/2015/05/18/runloop/...
    乜_啊_閱讀 1,682評(píng)論 0 5
  • 5月3日下午,武安市一景點(diǎn)古武當(dāng)山景區(qū)內(nèi)濃煙滾滾、火光沖天,原來(lái)是一座“碧霞寺”及周圍失火。事故發(fā)生后,武安市旅游...
    聚焦華夏閱讀 448評(píng)論 1 0

友情鏈接更多精彩內(nèi)容