你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS 中 run loop 淺析

iOS 中 run loop 淺析

編輯:IOS開發綜合

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的真實情況自然是復雜得多,便萬變不離其宗,你要有興趣,也可以研究研究它的實現。

 

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved