ios中timer相關的延時調用須要注意的地方

ios中timer相關的延時調用,常見的有兩種,一種是NSObject中的performSelector:withObject:afterDelay:以及performSelector:withObject:afterDelay:inModes:。這兩個方法在調用的時候會設置當前runloop中timer,前者設置的timer在NSDefaultRunLoopMode運行,後者則能夠指定NSRunLoop的mode來執行。還有一種延時調用,是直接配置timer來運行,利用NSTimer來配置任務。這兩種方式都一個共同的前提,就是當前線程裏面須要有一個運行的runloop而且這個runloop裏面有一個timer。
而咱們都知道的是,只有主線程會在建立的時候默認自動運行一個runloop,而且有timer,普通的子線程是沒有這些的。這樣就帶來一個問題了,有些時候咱們並不肯定咱們的模塊是否是會異步調用到,而咱們在寫這樣的延時調用的時候通常都不會去檢查運行時的環境,這樣在子線程中被調用的時候,咱們的代碼中的延時調用的代碼就會一直等待timer的調度,可是實際上在子線程中又沒有這樣的timer,這樣咱們的代碼就永遠不會被調到。
回過頭來看,在有多線程操做的環境中,這樣的延時調用實際上是缺少安全性的。咱們能夠用另外一套方案來解決這個問題,就是使用GCD中的dispatch_after來實現單次的延時調用: ios

double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self someMethod];
    });

dispatch_time的單位是納秒,爲防止用戶使用時候的出現換算上的錯誤,Xcode在開發者打dispatch_after的時候會自動補全上面一整段代碼。 安全

而若是有循環的調用的話,能夠用dispatch_source_set_timer來實現 多線程

int interval = 2;
int leeway = 0;

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (timer) {
    dispatch_source_set_timer(timer, dispatch_walltime(DISPATCH_TIME_NOW, NSEC_PER_SEC * interval), interval * NSEC_PER_SEC, leeway);
    dispatch_source_set_event_handler(timer, ^{
        [self someMethod];
    });
    dispatch_resume(timer);
}
這裏用到了GCD的事件源和事件監聽的機制,這個會另外詳細介紹
相關文章
相關標籤/搜索