主要讲解日常开发中定时器的选择;
iOS 内存管理 部分一
iOS 内存管理 部分二
iOS 内存管理 部分三
iOS 内存管理 部分四
1. 日常开发中定时器的选择
首先有个问题是NSTimer
是否准确? 答案是不准确, 因为NSTimer
不论是在主线程还是子线程都是依赖于Runloop
的, 就跟主线程刷新UI
一样, 我们写完UI
的刷新代码并不会立即执行, 而是等当前Runloop
周期结束时才会刷新, 所以NSTimer
也是这样, 如果某个Runloop
周期处理的事情较多而耗时过长则直接导致NSTimer
的时间变得不准确;
替代方案:使用内核级别的GCD
的timer
- (void)GCDTimerAction {
///创建一个队列
dispatch_queue_t queue = dispatch_get_main_queue();
///创建一个GCDTimer, 它的类型是DISPATCH_SOURCE, 注意Timer一定要强引用
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
/*
设置Timer的一些参数
参数1: 设置多久后触发timer
参数2: 设置间隔多久触发一次timer
*/
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 1 * NSEC_PER_SEC);
///timer的调用方法
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"触发GCDTimer");
});
///重置timer
dispatch_resume(self.timer);
}
下面我们对GCD
的Timer
进行下封装, 使其更方便使用;
#封装定时器的.h 文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface XTimer : NSObject
/// 创建一个定时器并启动 返回这个定时器的 key
/// @param task 需要执行的任务
/// @param begin 开始时间
/// @param interval 执行间隔
/// @param repeat 是否重复
/// @param async 是否异步执行
+ (NSString *)excuteTimerWithTask:(void(^)(void))task
begin:(double)begin
interval:(double)interval
repeat:(BOOL)repeat
async:(BOOL)async;
/// 取消hash值为hashStr的timer任务
+ (void)cancelTask:(NSString *)hashStr ;
@end
NS_ASSUME_NONNULL_END
#封装定时器的.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;
});
}
/// 创建一个定时器并启动 返回这个定时器的 key
/// @param task 需要执行的任务
/// @param begin 开始时间
/// @param interval 执行间隔
/// @param repeat 是否重复
/// @param async 是否异步执行
+ (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);
}
/// 根据 key 取消任务
+ (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);
}
///设定一个 key
NSString* Str(NSInteger count) {
return [NSString stringWithFormat:@"%ld", count];
}
@end
参考文章和下载链接
文中测试代码