1. 程式人生 > >實現定時器功能的幾種方式

實現定時器功能的幾種方式

  1. nsrunLoop
  2. GCD
  3. RAC

NsrunLoop

NSRunLoop是IOS訊息機制的處理模式

  • 一條執行緒對應一個RunLoop,主執行緒的RunLoop預設已經建立好了, 而子執行緒的需要我們自己手動建立
    • 獲取主執行緒對應的RunLoop物件mainRunLoop/CFRunLoopGetMain( [NSRunLoop mainRunLoop]
    • 獲取當前執行緒對應的RunLoop物件currentRunLoop/CFRunLoopGetCurrent ( [NSRunLoop currentRunLoop]
  • RunLoop會一直迴圈檢測,從執行緒start到執行緒end,檢測檢測到事件源(CFRunLoopSourceRef)執行處理函式,首先會產生通知,corefunction向執行緒新增runloopObservers來監聽事件,並控制NSRunLoop裡面執行緒的執行和休眠,在有事情做的時候使當前NSRunLoop控制的執行緒工作,沒有事情做讓當前NSRunLoop的控制的執行緒休眠。
-(void)demo1
{

    //事件 交給誰處理?Runloop
    NSTimer *timer =  [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timeMethod) userInfo:nil repeats:YES];

  [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
}
//所有被"標記"common的模式都可以執行,UITrackingRunLoopMode和kCFRunLoopDefaultMode都被標記為了common模式,所以只需要將timer的模式設定為forMode:NSRunLoopCommonModes,就可以在預設模式和追蹤模式都能夠執行
-(void)timeMethod { NSLog(@"能不能執行!"); }

NsrunLoop的mode

  • NSDefaultRunLoopMode:App的預設Mode,通常主執行緒是在這個Mode下執行(UI事件會阻塞定時器的執行)
  • UITrackingRunLoopMode:介面跟蹤Mode,用於ScrollView 追蹤觸控滑動,保證介面滑動時不受其他 Mode 影響
  • UIInitializationRunLoopMode:在剛啟動App時第進入的第一個 Mode,啟動完成後就不再使用
  • GSEventReceiveRunLoopMode: 接受系統事件的內部 Mode,通常用不到
  • NSRunLoopCommonModes:這是一個佔位用的Mode,不是一種真正的Mode

GCD

///將定時器設定在主執行緒
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));

    //2設定定時器每一秒執行一次 GCD時間事件 1000000000 
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0);
    3設定定時器執行的動作
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"----%@",[NSThread currentThread]);
        NSLog(@"執時鐘事件");
    });

    //4.啟動定時器
    dispatch_resume(timer);
    _timer = timer;//設定一個變數儲存timer 否則執行一次就被釋放了,是因為記憶體管理的原因,使用了dispatch_source_create方法,這種方法GCD是不會幫你管理記憶體的

dispatch_source_create

dispatch_source_create(dispatch_source_type_t type,
 uintptr_t handle,
 unsigned long mask,
 dispatch_queue_t _Nullable queue)
  • 第一個引數:dispatch_source_type_t type為設定GCD源方法的型別,這裡設定型別為定時器

    • type型別
    • DISPATCH_SOURCE_TYPE_DATA_ADD 變數增加
    • DISPATCH_SOURCE_TYPE_DATA_OR 變數 OR
    • DISPATCH_SOURCE_TYPE_MACH_SEND MACH埠傳送
    • DISPATCH_SOURCE_TYPE_MACH_RECV MACH埠接收
    • DISPATCH_SOURCE_TYPE_MEMORYPRESSURE 記憶體壓力 (注:iOS8後可用)
    • DISPATCH_SOURCE_TYPE_PROC 檢測到與程序相關的事件
    • DISPATCH_SOURCE_TYPE_READ 可讀取檔案映像
    • DISPATCH_SOURCE_TYPE_SIGNAL 接收訊號
    • DISPATCH_SOURCE_TYPE_TIMER 定時器
    • DISPATCH_SOURCE_TYPE_VNODE 檔案系統有變更
    • DISPATCH_SOURCE_TYPE_WRITE 可寫入檔案映像
  • 第二個引數:uintptr_t handle Apple的API介紹說,暫時沒有使用,傳0即可。

  • 第三個引數:unsigned long mask Apple的API介紹說,使用DISPATCH_TIMER_STRICT,會引起電量消耗加劇,畢竟要求精確時間,所以一般傳0即可,視業務情況而定。
  • 第四個引數:dispatch_queue_t _Nullable queue 佇列,將定時器事件處理的Block提交到哪個佇列之上。可以傳Null,預設為全域性佇列。注意:當提交到全域性佇列的時候,時間處理的回撥內,需要非同步獲取UI執行緒,更新UI

dispatch_source_set_timer

dispatch_source_set_timer(dispatch_source_t source,
 dispatch_time_t start,
 uint64_t interval,
 uint64_t leeway);
  • 第一個引數:dispatch_source_t source不用說了,傳上一步的timer
  • 第二個引數:dispatch_time_t start, 定時器開始時間,型別為 dispatch_time_t,其API的abstract標明可參照dispatch_time()和dispatch_walltime(),同為設定時間,但是後者為“鐘錶”時間,相對比較準確,所以選擇使用後者。dispatch_walltime(const struct timespec *_Nullable when, int64_t delta),引數when可以為Null,預設為獲取當前時間,引數delta為增量,即獲取當前時間的基礎上,增加X秒的時間為開始計時時間,此處傳0即可。 我們一般用DISPATCH_TIME_NOW現在開始
  • 第三個引數:uint64_t interval,定時器間隔時長,由業務需求而定。
  • 第四個引數:uint64_t leeway, 允許誤差,此處傳0即可。

dispatch_source_set_event_handler

dispatch_source_set_event_handler(dispatch_source_t source,
 dispatch_block_t _Nullable handler)
  • 第一個引數:dispatch_source_t source不用說了,傳上上一步的timer
  • 第二個引數:dispatch_block_t _Nullable handler,定時器執行的動作,需要處理的業務邏輯Block。

dispatch_resume

dispatch_resume(_timer)

定時器建立完成並不會執行,需要主動去觸發,也就是呼叫上述方法。

排程源提供了源事件的處理回撥,同時也提供了取消源事件處理的回撥,使用非常方便。

dispatch_source_set_cancel_handler(dispatch_source_t source,
 dispatch_block_t _Nullable handler)

這個方法引數一看就明白了

RAC

  RACDisposable* disble =   [[RACSignal interval:1.0 onScheduler:[RACScheduler scheduler]]subscribeNext:^(NSDate * _Nullable x) {

        NSLog(@"%@",[NSThread currentThread]);

    }];

    [disble dispose];//取消  比如點選什麼事件取消