本文主要根据NSTimer的五种初始化方法来进行介绍
一:定义一个NSTimer,下面这种方法创建的定时器不用加入RunLoop,就可以运行,只要调用就可以运行
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:
(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
// 开始定时器
- (void) startPaintingOne{
/*
seconds:需要调用的毫秒数
target:调用方法需要发送的对象。即:发给谁
userInfo:发送的参数
repeats:指定定时器是否重复调用目标方法
*/
// 定义一个NSTimer,此种方法创建的定时器不用加入RunLoop,就可以运行,只要调用就可以运行
self.paintingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(paint:) userInfo:nil
repeats:YES];
// 定义一个NSTimer,此种方法创建的定时必须加入到RunLoop中才可以运行,然后RunLoop再Run才行,详细使用方法见下面介绍
// self.paintingTimer = [NSTimer timerWithTimeInterval:1.0
// target:self
// selector:@selector(paint:)
// userInfo:nil
// repeats:YES];
}
二: 定义一个NSTimer,
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget
selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
此种方法创建的定时必须加入到RunLoop中才可以运行,然后RunLoop再Run才行,详细使用方法见下面介绍
/*------方法2---(在子线程执行定时器方法)-------------*/
- (void)startPaintingTwo{
self.paintingTimer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:@selector(paint:)
userInfo:nil
repeats:YES];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperationWithBlock:^{
[self startPaintingTwo];
NSRunLoop *loop = [NSRunLoop currentRunLoop];
[loop addTimer:self.paintingTimer forMode:NSDefaultRunLoopMode];
[loop run];
}];
}
三:使用
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:
(NSInvocation *)invocation repeats:(BOOL)yesOrNo
定义一个NSTimer
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:
(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
- (void) startPaintingThree{
// 定义将调用的方法
SEL selectorToCall = @selector(paint:);
// 为SEL进行 方法签名
NSMethodSignature *methodSignature =[[self class] instanceMethodSignatureForSelector:selectorToCall];
// 初始化NSInvocation
NSInvocation *invocation =[NSInvocation invocationWithMethodSignature:methodSignature];
[invocation setTarget:self];
[invocation setSelector:selectorToCall];
self.paintingTimer = [NSTimer timerWithTimeInterval:1.0
invocation:invocation
repeats:YES];
//主线程执行方法
// [[NSRunLoop currentRunLoop] addTimer:self.paintingTimer forMode:NSDefaultRunLoopMode];
//子线程执行方法
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperationWithBlock:^{
NSRunLoop *loop = [NSRunLoop currentRunLoop];
[loop addTimer:self.paintingTimer forMode:NSDefaultRunLoopMode];
[loop run];
}];
}
四:采用GCD定时器
GCD的定时器和NSTimer是不一样的,NSTimer受RunLoop影响,但是GCD的定时器不受影响,因为RunLoop也是基于GCD的
- (void)startPaintingFour{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(_timer, ^{
NSLog(@"这里的线程为%@",[NSThread currentThread]);
// 这里表示可以切回主线程,也可以就在子线程里面执行代码
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@--这里的代码每秒在执行一次么?",[[NSDate alloc]init]);
});
});
// 开启计时器
dispatch_resume(_timer);
}
// 销毁计时器
-(void)destoryTimer{
if (_timer) {
dispatch_source_cancel(_timer);
_timer = nil;
}
}
五:CADisplayLink
** CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。 CADisplayLink以特定模式注册到runloop后, 每当屏幕显示内容刷新结束的时候,runloop就会向 CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。**
iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。使用场合相对专一,适合做UI的不停重绘,比如自定义动画引擎或者视频播放的渲染。不需要在格外关心屏幕的刷新频率了,本身就是跟屏幕刷新同步的。
- (void)startPaintingFive{
self.displayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink{
NSLog(@"代码执行到这里了么");
}
// 注销计时器
- (void)stopDisplayLink{
[self.displayLink invalidate];
self.displayLink = nil;
}
六:全局计时器的使用,可以保证程序进入后台和再前台都能准确的保证计时器的运行
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)NSTimer *timer; // timer
@property(nonatomic,assign)int countDown; // 倒数计时用
@property(nonatomic,strong)NSDate *beforeDate; // 上次进入后台时间
@end
static int const tick = 60;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setupNotification];
[self startCountDown];
}
-(void)dealloc {
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
[self stopTimer];
}
-(void)setupNotification {
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(enterBG) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(enterFG) name:UIApplicationWillEnterForegroundNotification object:nil];
}
/**
* 进入后台记录当前时间
*/
-(void)enterBG {
NSLog(@"应用进入后台啦");
_beforeDate = [NSDate date];
}
/**
* 返回前台时更新倒计时值
*/
-(void)enterFG {
NSLog(@"应用将要进入到前台");
NSDate * now = [NSDate date];
int interval = (int)ceil([now timeIntervalSinceDate:_beforeDate]);
int val = _countDown - interval;
if(val > 1){
_countDown -= interval;
}else{
_countDown = 1;
}
}
/**
* 开始倒计时
*/
-(void)startCountDown {
_countDown = tick; ///< 重置计时
_timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES]; ///< 需要加入手动RunLoop,需要注意的是在NSTimer工作期间self是被强引用的
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; ///< 使用NSRunLoopCommonModes才能保证RunLoop切换模式时,NSTimer能正常工作。
}
-(void)timerFired:(NSTimer *)timer {
if (_countDown == 0) {
[self stopTimer];
NSLog(@"重新发送");
}else{
_countDown -=1;
NSLog(@"倒计时中:%d",_countDown);
}
}
- (void)stopTimer {
if (_timer) {
[_timer invalidate];
}
}
@end
网友评论