iOS 中 run loop 淺析

來源:互聯網
上載者:User

iOS 中 run loop 淺析
iOS 中 run loop 淺析

  runloop 雖然是與線程想關的重要概念,但 cocoa 中的 runloop 終是用得不多,觀相關博文卻也未得入門其“why”。所以淺習幾日,得一粗陋分享淺文,作為筆記,寫下其所以然。有不對或錯誤的地方,還望指教,不甚感激。

run loop解惑

  線程在執行完後,會被銷毀。為了使線程能一直運行,咱們可以線上程裡邊弄個運行迴圈(run loop),讓線程一直執行:

- (void)myThread:(id)sender{  while (TRUE) {    @autoreleasepool {      //do some jobs      //break in some condition      usleep(10000);    }  }}

  好了,現線上程能夠一直運行了。新任務來了:在這個線程啟動並執行同時,還可以從其它線程裡往它裡面隨意增加或去掉不同的計算任務。這就是 NSRunloop 強大的地方了。
  咱們現在來簡單地進化一下:

NSMutableArray *targetQueue;NSMutableArray *actionQueue;- (void)myThread:(id)sender{  while (TRUE) {    @autoreleasepool {      //do some jobs      //break in some condition      NSUInteger targetCount = [targetQueue count];      for(NSUInteger index = 0; index < targetCount; ++index){        id target = targetQueue[index];        SEL action = NSSelectorFromString(actionQueue[index]);        if ([target respondsToSelector:action]) {          [target performSelector:action withObject:nil];        }      }      usleep(10000);    }  }}

  從這裡,我們可以在其它線程中向 targetQueue和 actionQueue同時加入對象和方法時,這線程可以執行動態添加的代碼了。
  所謂runloop,就是下面這個結構:

while (TRUE) {    //break in some condition}

  這個結構就是線程的 runloop,它和 NSRunloop這個類的名字很像,但實際上完全不是一個 東西。那 NSRunloop是個啥東西呢?咱們再看以下代碼:

@interface MyNSTimer : NSObject{    id target;    SEL action;    float interval;    CFAbsoluteTime lasttime;}- (void)invoke;@end@implementation MyNSTimer- (void)invoke;{    if ([target respondsToSelector:action]) {        [target performSelector:action withObject:nil];    }}@end#!objc@interface MyNSRunloop : NSObject{    NSMutableArray *timerQueue;}- (void)addTimer:(MyNSTimer*)t;- (void)executeOnce;@end@implementation MyNSRunloop- (void)addTimer:(MyNSTimer*)t;{    @synchronized(timerQueue){        [timerQueue addObject:t];    }}- (void)executeOnce;{    CFAbsoluteTime currentTime=CFAbsoluteTimeGetCurrent();    @synchronized(timerQueue){        for(MyNSTimer *t in timerQueue){            if(currentTime-t.lasttime>t.interval){                t.lasttime=currentTime;                [t invoke];            }        }    }}@end@interface MyNSThread : NSObject{    MyNSRunloop *runloop;}- (void)main:(id)sender;@end@implementation MyNSThread- (void)main:(id)sender{  while (TRUE) {    @autoreleasepool {      //do some jobs      //break in some condition      [runloop executeOnce];      usleep(10000);    }  }}@end

  走到這裡,我們就算是基本把Runloop結構抽象出來了。例如我有一個MyNSThread執行個體,myThread1。我可以給這個執行個體的線程添加需要的任務,而myThread1內部的MyNSRunloop對象會管理好這些任務。

MyNSTimer *timer1=[MyNSTimer scheduledTimerWithTimeInterval:1 target:obj1 selector:@selector(download1:)];[myThread1.runloop addTimer:timer1];MyNSTimer *timer2=[MyNSTimer scheduledTimerWithTimeInterval:2 target:obj2 selector:@selector(download2:)];[myThread1.runloop addTimer:timer2];
run loop model解惑

  咱們知道,在 iOS中,使用者體驗是極其重要的。比如 UITableView在滑動時極為順暢,介面的更新是由主線程負責的,但即使咱們以預設的方式加再多的 NSTimer定時任務到主線程中,即也不會對 UITableView的滑動造成影響。主線程是怎麼做到這點的?這就跟 run loop model相關了。
  咱們再來改進一下代碼:

@interface MyNSRunloopMode : NSObject {  NSMutableArray *_timerQueue;  NSString *_name;}- (void)addTimer:(MyNSTimer *)timer;- (void)executeOnce;@end@interface MyNSRunloop : NSObject{  NSMutableSet *_modes;  MyNSRunloopMode *_currentMode;}- (void)addTimer:(MyNSTimer*)t;- (void)addTimer:(MyNSTimer *)t forMode:(NSString *)mode;- (void)executeOnce;@end// 實現檔案@implementation MyNSRunloopMode- (void)executeOnce {  CFAbsoluteTime currentTime=CFAbsoluteTimeGetCurrent();  @synchronized(timerQueue){    for(MyNSTimer *t in timerQueue){      if(currentTime-t.lasttime>t.interval){        t.lasttime=currentTime;        [t invoke];      }    }  }}@end@implementation MyNSRunloop- (void)addTimer:(MyNSTimer *)timer {  [self addTimer:timer forMode:@NSDefaultRunLoopMode];}- (void)addTimer:(MyNSTimer *)timer forMode:(NSString *)modeName {  MyNSRunloopMode *mode = nil;  for (mode in _modes) {    if ([mode.name isEqualToString:modeName]) {      break;    }  }  [mode addTimer:timer];}- (void)executeOnce {  [_currentMode executeOnce];}@end

  咱們又添加了一個類:MyNSRunloopMode,把原本在 NSRunloop的執行任務放到這個類的對象裡面去了。而 NSRunloop則有一個mode的集合,並有一個 currentMode。runloop在任何時候,都只可能運行在currentMode下,也就是說,它此時只會執行該 mode下的任務。咱們可以指定當前 NSRunloop的 mode:[NSRunLoop runMode:beforeDate:]
  系統給定義了好幾個mode,而每個 model都有自己的名字,其中有一個就是NSDefaultRunLoopMode。咱們在新增工作到run loop中時,只需要指定相應model的名字,就會把任務添加到相應的model中去了。
  我想你也猜到了吧,正是因為 NSTimer預設會加到NSDefaultRunLoopMode中,而在 UITableView滑動時,主線程卻不在這個 mode之下(而是切到NSEventTrackingRunLoopMode中),所以 NSTimer的任務壓根兒就不會被執行,當然也就不會對滑動有絲毫影響啦。在滑動完後,主線程又會切到NSDefaultRunLoopMode中,此時 NSTimer的任務又可以執行了。當然咱們還是可以將 NSTimer加到任何 mode之中:[NSRunLoop addTimer:forModel:]
  怎麼樣,這個 mode的引入還是蠻給力的吧!
  
  本文介紹了 run loop的基本的原理,runloop的真實情況自然是複雜得多,便萬變不離其宗,你要有興趣,也可以研究研究它的實現。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.