iOS macOS中的三大计时器(NStimer、CADisplayLink、dispatch_source_set_timer)
一、介绍
在iOS macOS中,计时器是比较常用的,用于统计累加数据或者倒计时等,例如手机号获取验证码。计时器大概有那么三种,分别是:NSTimer、CADisplayLink、dispatch_source_set_timer
二、使用
@property (strong,nonatomic)NSTimer *timer; @property (strong,nonatomic)CADisplayLink *displaylinkTimer; @property (strong,nonatomic)dispatch_source_set_timer sourceTimer;
1、NSTimer:
#define TimeInterval 5 //时间间隔,间隔显示
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, assign) int timerCount;
#pragma mark --定时器间隔5秒滚动一次,遇到新的股票代码则重新计时
#pragma mark - 开始计时1
-(void)starTime1{
[self stopTime1];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(repeatShowTime1:) userInfo:@"admin" repeats:YES];
}
#pragma mark - 停止计时1
-(void)stopTime1{
if (self.timer) {
[self.timer invalidate];//销毁定时器
self.timer = nil;//销毁定时器
}
self.timerCount = 0;
}
#pragma mark - 计时1
- (void)repeatShowTime1:(NSTimer *)tempTimer {
self.timerCount++;
//5秒
if (self.timerCount == TimeInterval) {
[self action1];
}
//6秒
if(self.timerCount == TimeInterval + 1){
[self action2];
}
if(self.timerCount == TimeInterval * 2 + 1){
[self action3];
}
if(self.timerCount == TimeInterval * 2 + 1 + 1){
[self action4];
[self stopTime1];//停止计时,调用
[self starTime1];//开始计时,调用
}
}
2、CADisplayLink:
1、创建CADisplayLink对象
要使用CADisplayLink,我们首先需要创建一个CADisplayLink对象。创建CADisplayLink对象非常简单,只需要调用CADisplayLink的类方法displayLinkWithTarget:selector:即可。
// 创建CADisplayLink对象 CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];
其中,displayLinkWithTarget:selector:方法中的target是指定接收回调方法的对象,而selector则是回调方法的名称。
2、启动CADisplayLink
创建好CADisplayLink对象后,需要调用CADisplayLink的方法addtoRunLoop:forMode:来启动它。参数中的RunLoop指的是视图所在的Run Loop对象,forMode指的是RunLoop的模式,可以设置为NSDefaultRunLoopMode或UITrackingRunLoopMode。
// 将CADisplayLink添加到RunLoop中 [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
3、实现回调方法
启动CADisplayLink后,我们需要实现回调方法来控制视图的更新。CADisplayLink提供了一个叫做回调方法update:的方法,它会在每一帧渲染之前被调用,并且它的参数就是CADisplayLink对象本身。
- (void)update:(CADisplayLink *)displayLink { // 更新视图代码 }
在更新视图的代码中,我们可以根据需要设置视图的状态,从而实现各种类型的动画效果。
1、使用CADisplayLink实现动画
下面是一个使用CADisplayLink实现移动动画的示例代码。
- (void)initView { // 创建视图 UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)]; view.backgroundColor = [UIColor redColor]; [self.view addSubview:view]; // 创建CADisplayLink对象 CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateViewPosition:)]; // 启动CADisplayLink [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; } // 更新视图位置的回调方法 - (void)updateViewPosition:(CADisplayLink *)displayLink { static CGFloat x = 0; x += 1; if (x > CGRectGetWidth(self.view.bounds)) { x = 0; } self.view.subviews.firstObject.center = CGPointMake(x, 100); }
上述代码中,我们创建了一个正方形的视图,并通过CADisplayLink实现了每一帧都向右移动一个像素的动画效果。
2、使用CADisplayLink刷新视图
下面是一个使用CADisplayLink刷新视图的示例代码。
- (void)initView { // 创建视图 self.imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; self.imageView.image = [UIImage imageNamed:@"image.jpg"]; [self.view addSubview:imageView]; // 创建CADisplayLink对象 CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(refreshView)]; // 启动CADisplayLink [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; } // 刷新视图的回调方法 - (void)refreshView { // 刷新视图 [self.imageView setNeedsDisplay]; }
上述代码中,我们创建了一个UIImageView视图,并通过CADisplayLink定时刷新它。每当回调方法被触发时,我们都会调用UIView的setNeedsDisplay方法来刷新UIView,从而让它在设备屏幕上进行重新绘制。
CADisplayLink的使用方法,包括如何创建和启动CADisplayLink,如何使用它来执行动画和实现视图刷新等。在实际开发中,CADisplayLink是一个非常强大的类,可以用于实现各种类型的动画效果和视图刷新,希望读者能够充分利用它的功能,为iOS应用程序带来更加出色的效果。
3.dispatch_source_set_timer
GCD 定时器
CADisplayLink、NSTimer是基于RunLoop机制的,如果RunLoop的任务过于繁重,有可能会导致前两个定时器不准时。
举个例子:
加入我们创建了一个NSTimer定时器,每1秒钟做任务。那么,什么时候执行NSTimer呢?
是在RunLoop跑圈的过程中执行NSTimer定时器,而RunLoop跑完一圈执行的时间不固定,也就导致有可能1秒钟过去了,但是RunLoop还没有执行到定时器的任务,那么,这就造成定时器有可能不准时。
一、GCD 定时器
GCD是不依赖与RunLoop,是直接跟系统内核交互的。时间比较准确。
GCD 定时器简单的使用:
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"begin"); // 队列 dispatch_queue_t queue = dispatch_get_main_queue(); // 创建定时器 dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); // 设置时间 uint64_t start = 2.0; uint64_t interval = 1.0; dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC), interval * NSEC_PER_SEC, 0); // 设置回调 dispatch_source_set_event_handler(timer, ^{ NSLog(@"111"); }); // 启动定时器 dispatch_resume(timer); self.timer = timer; }
2022-07-05 17:42:46.674345+0800 Interview02-GCD定时器[13943:350556] begin
2022-07-05 17:42:48.675440+0800 Interview02-GCD定时器[13943:350556] 111
2022-07-05 17:42:49.675542+0800 Interview02-GCD定时器[13943:350556] 111
2022-07-05 17:42:50.675350+0800 Interview02-GCD定时器[13943:350556] 111
2022-07-05 17:42:51.674523+0800 Interview02-GCD定时器[13943:350556] 111
二、GCD 定时器的实现方案
第一步:封装
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface RHGCDTimer : NSObject + (NSString *)timerWithBlockTask:(void(^)(void))blockTask star:(float)star interval:(float)interval repeat:(BOOL)repeat async:(BOOL)async; + (void)cancelTask:(NSString *)name; @end NS_ASSUME_NONNULL_END
#import "RHGCDTimer.h" static NSMutableDictionary *timersDict; static dispatch_semaphore_t semaphore; @implementation RHGCDTimer + (void)initialize { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ timersDict = [NSMutableDictionary dictionary]; semaphore = dispatch_semaphore_create(1);//创建一个信号量,只允许一个线程操作 }); } + (NSString *)timerWithBlockTask:(void (^)(void))blockTask star:(float)star interval:(float)interval repeat:(BOOL)repeat async:(BOOL)async { if (!blockTask || star<0 || (repeat && interval <= 0)) return nil; //创建队列,队列决定到时候任务是在哪个线程执行 dispatch_queue_t queue = async ? dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL) : dispatch_get_main_queue(); //创建一个定时器 dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); /** dispatch_source_set_timer 上面的定时器 dispatch_time_t start 开始时间 (typedef uint64_t dispatch_time_t;) uint64_t interval 间隔 uint64_t leeway 误差一般写0 */ dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, star * NSEC_PER_SEC), interval *NSEC_PER_SEC, 0); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//信号量 //定时器唯一标识 static int i = 0; NSString *name = [NSString stringWithFormat:@"%d", i++]; //放进字典,就会产生强引用 timersDict[name] = timer; dispatch_semaphore_signal(semaphore); //设置回调 dispatch_source_set_event_handler(timer, ^{ blockTask(); if (!repeat) {//如果非重复执行 [self cancelTask:name];//取消定时器 } }); //启动定时器 dispatch_resume(timer); //GCD不需要销毁 return name; } + (void)cancelTask:(NSString *)name { if (name.length == 0) return; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_source_t timer = timersDict[name]; if (!timer) return; dispatch_source_cancel(timer); [timersDict removeObjectForKey:name]; dispatch_semaphore_signal(semaphore); } @end
第二步:使用
#import "ViewController.h" #import "RHGCDTimer.h" @interface ViewController () @property (copy, nonatomic) NSString *task; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.task = [RHGCDTimer timerWithBlockTask:^{ NSLog(@"执行任务---%@", [NSThread currentThread]); } star:2.0 interval:1.0 repeat:YES async:YES]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [RHGCDTimer cancelTask:self.task]; } @end
第三步:测试验证
2022-07-05 17:31:41.375918+0800 Interview02-GCD定时器[13519:337316] 执行任务---<_NSMainThread: 0x600002448880>{number = 1, name = main}
2022-07-05 17:31:42.375935+0800 Interview02-GCD定时器[13519:337316] 执行任务---<_NSMainThread: 0x600002448880>{number = 1, name = main}
2022-07-05 17:31:43.375871+0800 Interview02-GCD定时器[13519:337316] 执行任务---<_NSMainThread: 0x600002448880>{number = 1, name = main}
到此这篇关于iOS中GCD定时器详解的文章就介绍到这了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix