主要講解日常開發(fā)中定時(shí)器的選擇;
iOS 內(nèi)存管理 部分一
iOS 內(nèi)存管理 部分二
iOS 內(nèi)存管理 部分三
iOS 內(nèi)存管理 部分四
1. 日常開發(fā)中定時(shí)器的選擇
首先有個問題是NSTimer
是否準(zhǔn)確? 答案是不準(zhǔn)確, 因?yàn)?code>NSTimer不論是在主線程還是子線程都是依賴于Runloop
的, 就跟主線程刷新UI
一樣, 我們寫完UI
的刷新代碼并不會立即執(zhí)行, 而是等當(dāng)前Runloop
周期結(jié)束時(shí)才會刷新, 所以NSTimer
也是這樣, 如果某個Runloop
周期處理的事情較多而耗時(shí)過長則直接導(dǎo)致NSTimer
的時(shí)間變得不準(zhǔn)確;
替代方案:使用內(nèi)核級別的GCD
的timer
- (void)GCDTimerAction {
///創(chuàng)建一個隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
///創(chuàng)建一個GCDTimer, 它的類型是DISPATCH_SOURCE, 注意Timer一定要強(qiáng)引用
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
/*
設(shè)置Timer的一些參數(shù)
參數(shù)1: 設(shè)置多久后觸發(fā)timer
參數(shù)2: 設(shè)置間隔多久觸發(fā)一次timer
*/
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 1 * NSEC_PER_SEC);
///timer的調(diào)用方法
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"觸發(fā)GCDTimer");
});
///重置timer
dispatch_resume(self.timer);
}
下面我們對GCD
的Timer
進(jìn)行下封裝, 使其更方便使用;
#封裝定時(shí)器的.h 文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface XTimer : NSObject
/// 創(chuàng)建一個定時(shí)器并啟動 返回這個定時(shí)器的 key
/// @param task 需要執(zhí)行的任務(wù)
/// @param begin 開始時(shí)間
/// @param interval 執(zhí)行間隔
/// @param repeat 是否重復(fù)
/// @param async 是否異步執(zhí)行
+ (NSString *)excuteTimerWithTask:(void(^)(void))task
begin:(double)begin
interval:(double)interval
repeat:(BOOL)repeat
async:(BOOL)async;
/// 取消hash值為hashStr的timer任務(wù)
+ (void)cancelTask:(NSString *)hashStr ;
@end
NS_ASSUME_NONNULL_END
#封裝定時(shí)器的.m 文件
#import "XTimer.h"
#import <os/lock.h>
static NSMutableDictionary *timerDic;
static os_unfair_lock lock;
static NSInteger timerCount;
@implementation XTimer
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
timerDic = [NSMutableDictionary dictionaryWithCapacity:0];
lock = OS_UNFAIR_LOCK_INIT;
timerCount = 0;
});
}
/// 創(chuàng)建一個定時(shí)器并啟動 返回這個定時(shí)器的 key
/// @param task 需要執(zhí)行的任務(wù)
/// @param begin 開始時(shí)間
/// @param interval 執(zhí)行間隔
/// @param repeat 是否重復(fù)
/// @param async 是否異步執(zhí)行
+ (NSString *)excuteTimerWithTask:(void(^)(void))task
begin:(double)begin
interval:(double)interval
repeat:(BOOL)repeat
async:(BOOL)async {
///一些判斷條件
if (!task || begin < 0 || (interval <= 0 && repeat)) {
return nil;
}
dispatch_queue_t queue = async ? dispatch_get_global_queue(0, 0 ) : dispatch_get_main_queue();
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
os_unfair_lock_lock(&lock);
[timerDic setValue:timer forKey:Str(timerCount)];
timerCount ++;
os_unfair_lock_unlock(&lock);
NSLog(@"%@", timerDic.description);
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, begin * NSEC_PER_SEC, interval * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
task();
if (!repeat) {
[self cancelTask:Str(timerCount)];
}
});
dispatch_resume(timer);
return Str(timerCount);
}
/// 根據(jù) key 取消任務(wù)
+ (void)cancelTask:(NSString *)hashStr {
if (!hashStr) {
return;
}
os_unfair_lock_lock(&lock);
dispatch_source_t source = timerDic[hashStr];
if (!source) {
return;
}
dispatch_source_cancel(source);
[timerDic removeObjectForKey:hashStr];
timerCount --;
os_unfair_lock_unlock(&lock);
}
///設(shè)定一個 key
NSString* Str(NSInteger count) {
return [NSString stringWithFormat:@"%ld", count];
}
@end
參考文章和下載鏈接
文中測試代碼