iOS 詳解RunLoop

Runloop :運行循環

  1. APP啓動,操作系統會開啓一條線程,這就是這個APP的主線程;
  2. 這個主線程是一個常駐線程,因爲這條線程上邊的Runloop 被開啓了;

 

Runloop 作用

  1. 保證線程不退出;
  2. 負責監聽所有的事件。 如: 觸摸、時鐘、網絡事件...

 

Runloop 的模式(Mode)

  1. NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeSelect) userInfo:nil repeats:YES];
  2. [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeSelect) userInfo:nil repeats:YES];

 

    以上這兩個timer的實現, 結果是: 函數1不會執行,函數2會執行。原因就是函數2內部已經添加了runLoop。而函數1, 需要手動添加。 那麼函數1需要再添加: [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 即可執行time操作。

  •     Runloop共有5中模式:(常用的其實只有兩種)
  •     NSDefaultRunLoopMode        // 默認模式
  •     UITrackingRunLoopMode        // UI模式, 優先級高於默認模式
  •     NSRunLoopCommonModes    // 佔位模式,(並不是runloop 的真正模式,但功能相當於 默認模式+UI模式)
  •     程序初始化模式                         // 操作系統調用
  •     程序內核模式                             // 操作系統調用

 NSDefaultRunLoopMode  :  首先在沒有事件發生時候runloop處於休眠狀態,當有事件需要執行或響應, runloop會被喚醒做一次執行循環,執行結束繼續處於休眠狀態。 如果此時有觸摸事件發生( 比如 拖拽事件或者觸摸事件)那麼runloop 會優先處理觸摸事件,而忽略timer事件。timer 的selector 也不會被執行。當鬆開手 timer 會繼續執行。

UITrackingRunLoopMode :簡稱 UI模式, runloop 只會觸發UI事件,當觸摸事件發生的時候才runloop 纔會執行。而此時 默認模式即使有事件執行,runloop也不會理會。

NSRunLoopCommonModes: 佔位模式,並不是lunloop真正的模式。 所謂佔位指的是在添加了NSDefaultRunLoopMode 模式,也添加了TrackingRunLoopMode 模式。 那麼此時表現也是二合一。 正常執行的timer在發生觸摸時也會繼續執行。但是!!!timer 中如果有耗時操作, 將會造成UI頁面卡頓。 怎麼解決呢? 當然是放在子線程中!

 

Runloop 與多線程

   NSThread *tread =  [[NSThread currentThread]initWithBlock:^{

        NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeSelect) userInfo:nil repeats:YES];

       [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

       [[NSRunLoop currentRunLoop]run];

    }];

    [tread start];

 這樣  當前子線程的runloop 會運行起來!子線程會一直存在,即使timer停止。 相當於一個死循壞!!!

那麼要想銷燬當前子線程,記得在子線程中調用  [NSThread exit];  

如果在主線程中調用  [NSThread exit], 那麼主線程會退出, 子線程依舊還在運行。隨着主線程的退出,頁面點擊、拖拽等事件都不會再響應。那爲什麼主線程掛掉之後爲什麼UI頁面都還在顯示? 因爲UI顯示是顯示在操作系統上,UIApplication無法處理界面上的事情了,而界面是傳遞給主線程,主線程已掛斷掉 此時runloop 不在處理界面上任何事情。

 

Runloop 的source

source: 事件源(輸入源)按照函數調用棧分爲

             source0:非系統內核事件;

             source1: 系統內核事件;

    

GCD timer, 直接上代碼吧


    // 創建   
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
    // 設置timer 時間單位是納秒    1秒 = 1000000000納秒
    dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1.0*NSEC_PER_SEC, 0);
    // 設置回調
    dispatch_source_set_event_handler(self.timer, ^{
        NSLog(@"current thread ---   %@",[NSThread currentThread]);
    });
    // 啓動
    dispatch_resume(self.timer);

根據執行結果可知, GCD timer  默認已經將Runloop 添加進去了。

 

Runloop 的Observer

用於觀察runloop循環, 代碼  :


- (void)viewDidLoad {
    [super viewDidLoad];

// 防止runloop 休眠
    [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(timerMeather) userInfo:nil repeats:YES];
 
   // 添加觀察者
    [self addRunLoopObserver];
}

-(void)timerMeather
{
    
}

-(void)addRunLoopObserver{
    // 獲取當前RunLoop
    CFRunLoopRef runloop = CFRunLoopGetCurrent();
    // 定義一個context
    CFRunLoopObserverContext context = {
        0,
        (__bridge void *)self,
        &CFRetain,
        CFRelease,
        NULL
    };
    // 定義觀察者
    static CFRunLoopObserverRef defaultModeObserver;
    //創建觀察者
    defaultModeObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting, YES, 0, &callBack, &context);
    // 添加觀察者
    CFRunLoopAddObserver(runloop, defaultModeObserver, kCFRunLoopDefaultMode);
}

// 觀察runloop回調
static void callBack(CFRunLoopObserverRef obderver,CFRunLoopActivity activity ,void *info){

  // info 當前控制器,  添加觀察者時添加的context目的。
    NSLog(@"come ");
}


 添加代碼會發現,剛啓動的時候回執行幾次,然後就不執行了,或者偶爾執行以下。 那是因爲runloop 沒任務執行,處於休眠狀態。  那麼我們給執行一個timer,讓timer運行起來。 此時runloop 也就不會休眠了。

 

 

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章