大家平時開發(fā)中使用最多的定時器應該是NSTimer了,但是,NSTimer同時也存在一些弊端:比如,有時候你要把它添加到不同的runloop model上才能保證它正常執(zhí)行,又或者使用不當導致?lián)碛兴膶ο鬅o法釋放,又或者NSTimer本身的機制導致它并不是很精確。
下面就介紹一下GCD定時器的實現(xiàn):
GCD定時器其實是一種特殊的分派源,它是基于分派隊列的,而NSTimer是基于運行循環(huán)的,所以,尤其是在多線程中,GCD定時器要比NSTimer好用的多。另外,GCD定時器使用dispatch_block_t,而不是方法選擇器。
@interface GCDTimer : NSObject
+(GCDTimer *)repeatingTimerWithTimeInterval:(NSTimeInterval)seconds block:(dispatch_block_t)block;
@end
@interface GCDTimer()
@property (nonatomic, strong) dispatch_block_t block;
@property (nonatomic, strong) dispatch_source_t source;
@end
@implementation GCDTimer
+(GCDTimer *)repeatingTimerWithTimeInterval:(NSTimeInterval)seconds block:(dispatch_block_t)block {
GCDTimer *timer = [[self alloc] init];
timer.block = block;
timer.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); // 首先,創(chuàng)建一個定時器分派源并綁定到主分派隊列上,這使得定時器總是在主線程上觸發(fā)
uint64_t nsec = (uint64_t)(seconds * NSEC_PER_SEC);
dispatch_source_set_timer(timer.source, dispatch_time(DISPATCH_TIME_NOW, nsec), nsec, 0);//設置定時器
dispatch_source_set_event_handler(timer.source, block);//設置事件處理程序
dispatch_resume(timer.source);//打開定時器(分派源通常都是需要配置的,所以它們創(chuàng)建的時候處于暫停狀態(tài),只有resume之后才會開始發(fā)送事件)
return timer;
}
-(void)invalidate {
if (self.source) {
dispatch_source_cancel(self.source);
self.source = nil;
}
self.block = nil;
}
-(void)dealloc {
[self invalidate];//銷毀時將定時器設置為無效
}
@end
GCD定時器在各種runloop model下都是可以執(zhí)行的,因為它并不依賴與此,而NStimer,比如UIScrollView滾動的時候就需要添加到特定的model上才能執(zhí)行。
如果要后臺執(zhí)行定時器,只要添加一下設置即可:
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication* app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (bgTask != UIBackgroundTaskInvalid) {
bgTask = UIBackgroundTaskInvalid;
}
});
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
if (bgTask != UIBackgroundTaskInvalid) {
bgTask = UIBackgroundTaskInvalid;
}
});
});
}
<br /><br />
<br /><br /><br />
下面是swift 3.0的寫法:
class GCDTimer {
var block: () -> Void
var source: DispatchSourceTimer
init(block: @escaping ()->Void, source: DispatchSourceTimer) {
self.block = block
self.source = source
}
class func repeatingTimer(timeInterval seconds: Double, block: @escaping () -> Void) -> GCDTimer {
let source = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
let timer = GCDTimer(block: block, source: source)
timer.source.scheduleRepeating(deadline: DispatchTime.now(), interval: seconds)
timer.source.setEventHandler(handler: block)
timer.source.resume()
return timer
}
deinit {
self.source.cancel()
}
}
后臺執(zhí)行:
func applicationDidEnterBackground(_ application: UIApplication) {
let app = UIApplication.shared
var bgTask: UIBackgroundTaskIdentifier!
bgTask = app.beginBackgroundTask(expirationHandler: {
DispatchQueue.main.async {
if bgTask != UIBackgroundTaskInvalid {
bgTask = UIBackgroundTaskInvalid
}
}
})
DispatchQueue.global().async {
DispatchQueue.main.async {
if bgTask != UIBackgroundTaskInvalid {
bgTask = UIBackgroundTaskInvalid
}
}
}
}
祝大家玩的愉快!