大家平時(shí)開發(fā)中使用最多的定時(shí)器應(yīng)該是NSTimer了扬绪,但是妻往,NSTimer同時(shí)也存在一些弊端:比如侵状,有時(shí)候你要把它添加到不同的runloop model上才能保證它正常執(zhí)行蔚龙,又或者使用不當(dāng)導(dǎo)致?lián)碛兴膶?duì)象無法釋放青柄,又或者NSTimer本身的機(jī)制導(dǎo)致它并不是很精確伐债。
下面就介紹一下GCD定時(shí)器的實(shí)現(xiàn):
GCD定時(shí)器其實(shí)是一種特殊的分派源预侯,它是基于分派隊(duì)列的,而NSTimer是基于運(yùn)行循環(huán)的泳赋,所以雌桑,尤其是在多線程中,GCD定時(shí)器要比NSTimer好用的多祖今。另外校坑,GCD定時(shí)器使用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)建一個(gè)定時(shí)器分派源并綁定到主分派隊(duì)列上,這使得定時(shí)器總是在主線程上觸發(fā)
uint64_t nsec = (uint64_t)(seconds * NSEC_PER_SEC);
dispatch_source_set_timer(timer.source, dispatch_time(DISPATCH_TIME_NOW, nsec), nsec, 0);//設(shè)置定時(shí)器
dispatch_source_set_event_handler(timer.source, block);//設(shè)置事件處理程序
dispatch_resume(timer.source);//打開定時(shí)器(分派源通常都是需要配置的徐绑,所以它們創(chuàng)建的時(shí)候處于暫停狀態(tài)邪驮,只有resume之后才會(huì)開始發(fā)送事件)
return timer;
}
-(void)invalidate {
if (self.source) {
dispatch_source_cancel(self.source);
self.source = nil;
}
self.block = nil;
}
-(void)dealloc {
[self invalidate];//銷毀時(shí)將定時(shí)器設(shè)置為無效
}
@end
GCD定時(shí)器在各種runloop model下都是可以執(zhí)行的,因?yàn)樗⒉灰蕾嚺c此傲茄,而NStimer毅访,比如UIScrollView滾動(dòng)的時(shí)候就需要添加到特定的model上才能執(zhí)行。
如果要后臺(tái)執(zhí)行定時(shí)器盘榨,只要添加一下設(shè)置即可:
- (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()
}
}
后臺(tái)執(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
}
}
}
}
祝大家玩的愉快喻粹!