實際項目開發中經常會遇到延遲某件任務的執行,或者讓某件任務周期性的執行。然后也會在某些時候需要取消掉之前延遲執行的任務。
iOS中延遲操作有三種解決方案:
1、NSObject的方法:(對象方法)
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
2、使用NSTimer的方法:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
//需要手動添加到運行循環
------------------------------------------------------------------------------
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
//創建后會默認添加到NSDefaultRunLoopMode中,這個方法創建的定時器不會自動銷毀,需要手動銷毀,會被self強引用着,不特殊處理就會產生強引用循環,造成內存泄露. 一定不要使用這個方法,請使用下面的方法替代這個方法.
------------------------------------------------------------------------------
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
//創建后會默認添加到NSDefaultRunLoopMode中,添加到block中,系統回自己處理(不會強引用),系統會調用dealloc方法我們在此處銷毀timer即可
------------------------------------------------------------------------------
模式:以下兩種模式同一時刻只能是一種模式
NSDefaultRunLoopMode(默認模式)
UITrackingRunLoopMode(如果控制器的view上面有滾動視圖,但手指拖拽滾動視圖的時候,就會進入該模式.一般不會將定時器加入到這個模式中,如果想在滾動視圖的時候,定時器同樣起效一般會加入到下面的模式)
---------------------------------------------------------------------------------------
NSRunLoopCommonModes:上面兩個模式都能運行
3、使用GCD的方法:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//延遲執行的代碼
});
一般情況下,我們選擇使用GCD的dispatch_after。
因為如果不用GCD,需要注意以下三個細節:
1.必須保證有一個活躍的runloop。
當一個應用啟動時,系統會開啟一個主線程,並且把主線程跑起來,並且主線程的runloop是不會停止的。所以,當這兩個方法在主線程可以被正常調用。但實際編程中大部分邏輯處理是放在子線程中執行的。而子線程的runloop是默認關閉的。如果不手動激活runloop,performSelector和scheduledTimerWithTimeInterval的調用將是無效的。
2.NSTimer、performSelector的創建與撤銷必須在同一個線程操作。
3.內存有潛在泄露的風險
4.NSTimer相對於Dispatch定時器來說不准時.
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
//當然使用這個方法,不會產生強引用循環(系統已經幫我們處理了),我們只需要在對應的dealloc方法中銷毀定時器就OK了
但是dispatch_after有個致命的弱點:dispatch_after一旦執行后,就不能撤銷了。
其實GCD也有timer的功能。
// 1.獲得隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//dispatch_queue_t queue = dispatch_get_main_queue();
// 2.創建一個定時器(dispatch_source_t本質還是個OC對象
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 3.設置timer執行的事件
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW,1.0 * NSEC_PER_SEC);//1.0秒之后開始執行
uint64_t interval = (uint64_t)(2.0 * NSEC_PER_SEC);//每隔2.0秒執行一次
dispatch_source_set_timer(self.timer, start, interval, 0);
//4. 設置回調
dispatch_source_set_event_handler(self.timer, ^{
// 取消timer 或者做其他事情
dispatch_cancel(self.timer);
self.timer = nil;
});
//5.啟動定時器/激活timer
dispatch_resume(self.timer);
這樣我們就規避了NSTimer的三個缺陷。
我靠... 這也太復雜了!!! 而且還沒有repeats選項
我們能不能像NSTimer那樣使用呢?答案:當然有了!!!
沒錯! 我們將重復的代碼封裝起來,開放幾個供外界調用的參數!
有了思路寫代碼就很簡單了!
更多內容--> 博客導航 每周一篇喲!!!
有任何關於iOS開發的問題!歡迎下方留言!!!或者郵件lieryangios@126.com 雖然我不一定能夠解答出來,但是我會請教iOS開發高手!!!解答您的問題!!!
詳細設計請看下一篇: Object-C定時器,封裝GCD定時器的必要性!!! (二)