很詳細(xì)的計時器文章 介紹了三種定時器方法
http://www.itdecent.cn/p/c167ca4d1e7e
-
NSTimer
必須加入Runloop、
存在延遲、
UIScrollView滑動會暫停計時(若不想被UIScrollView滑動影響,需要將 timer 添加再到 UITrackingRunLoopMode 或 直接添加到NSRunLoopCommonModes 中) - GCD計時器 精度最好 ?
-
CADisplayLink
刷新頻率固定 (取決CPU的忙碌程度)、
屏幕刷新時調(diào)用 (CADisplayLink在正常情況下會在每次刷新結(jié)束都被調(diào)用,精確度相當(dāng)高。但如果調(diào)用的方法比較耗時,超過了屏幕刷新周期,就會導(dǎo)致跳過若干次回調(diào)調(diào)用機(jī)會)、
適合做界面渲染(CADisplayLink可以確保系統(tǒng)渲染每一幀的時候我們的方法都被調(diào)用,從而保證了動畫的流暢性。)
后臺常駐 的問題 看到了一篇文章
https://www.cnblogs.com/lyanet/archive/2013/03/26/2983079.html
還有另一篇
http://www.itdecent.cn/p/d1ecc467faff
通過這兩個
蘋果文檔說到任何應(yīng)用都有3分鐘的后臺執(zhí)行任務(wù)的時間 。。。跟蘋果借點時間用用。。。不用配置info.plist 避免因為配置后臺長期運行后,蘋果審查出你濫用api(如果你配置了 項目中必須要有相關(guān)的功能)
第一篇提到了 應(yīng)該在長期任務(wù)結(jié)束后,我們需要做一些事情進(jìn)行清理:
結(jié)束所有的線程和定時器,不管他們是基礎(chǔ)定時器還是GCD中創(chuàng)建的。
調(diào)用UIApplication的endBackgroundTask:方法來結(jié)束后臺任務(wù)。
將任務(wù)標(biāo)識設(shè)置為UIBackgroundTaskInvalid,標(biāo)志我們的任務(wù)結(jié)束。
最后,當(dāng)我們的應(yīng)用回到前臺,如果我們的后臺任務(wù)還在執(zhí)行中,我們需要確保我們在干掉它:
主要代碼放在下邊以防丟失
- 3分鐘的后臺執(zhí)行任務(wù)
@property (nonatomic, unsafe_unretained) UIBackgroundTaskIdentifier backgroundTaskIdentifier;
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication* app = [UIApplication sharedApplication];
__weak typeof(self)weakSelf = self;
self.backgroundTaskIdentifier = [app beginBackgroundTaskWithExpirationHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{ if (weakSelf.backgroundTaskIdentifier != UIBackgroundTaskInvalid)
{
weakSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
});
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{ if (weakSelf.backgroundTaskIdentifier != UIBackgroundTaskInvalid)
{
weakSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
});
});
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid){
[self endBackgroundTask];
}
}
- (void) endBackgroundTask{
dispatch_queue_t mainQueue = dispatch_get_main_queue();
__weak typeof(self)weakSelf = self;
dispatch_async(mainQueue, ^(void) {
if (weakSelf != nil){
[[UIApplication sharedApplication] endBackgroundTask:weakSelf.backgroundTaskIdentifier];
weakSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
});
}
#pragma mark - 定時器(GCD)
- (void)creatTimer{
//設(shè)置倒計時時間
//__block 如果修飾指針時,指針相當(dāng)于弱引用,指針對指向的對象不產(chǎn)生引用計數(shù)的影響
//通過檢驗發(fā)現(xiàn),方法調(diào)用后,timeout會先自動-1,所以如果從30秒開始倒計時timeout應(yīng)該寫31
__block int timeOut = 31;
//獲取全局隊列
dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//創(chuàng)建一個定時器,并將定時器的任務(wù)交給全局隊列執(zhí)行(并行,不會造成主線程阻塞)
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, global);
//設(shè)置觸發(fā)的間隔時間
// dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0); //每秒執(zhí)行
//1.0 * NSEC_PER_SEC 代表設(shè)置定時器觸發(fā)的時間間隔為1s
//0 * NSEC_PER_SEC 代表時間允許的誤差是 0s
//block內(nèi)部 如果對當(dāng)前對象的強(qiáng)引用屬性修改,應(yīng)該使用 __weak typeof(self)weakSelf 修飾 避免循環(huán)引用
__weak typeof(self)weakSelf = self;
//設(shè)置定時器觸發(fā)事件
dispatch_source_set_event_handler(timer, ^{
//倒計時 刷新UI,當(dāng)?shù)褂嫊r為0時,結(jié)束倒計時
//1.每調(diào)用一次 時間-1
timeOut --;
//2.對timeOut 進(jìn)行判斷時間是停止倒計時 還是修改UI
if (timeOut <= 0) {
//停止倒計時時
//關(guān)閉定時器
dispatch_source_cancel(timer);
//主線程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
});
}else{
//主線程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
});
}
});
dispatch_resume(timer);
}