iOS定时器(时间不在于你拥有多少,而在于你怎样使用)
开篇
很喜欢某个游戏英雄的一句台词“时间不在于你拥有多少,而在于你怎样使用”。今天我们就来介绍一下时间的使用方式定时器,死板的定理向来不是我的风格今天我们就通过一个例子为开头来说明一下定时的使用,但绝不仅仅于此。
验证码.png
上面两张图展示的就是一个注册账号时发生手机短信验证码的界面,而获取验证码的倒计时就是用的定时器来实现的。
定时器简单实现验证码倒计时
为了能够很好的演示,我们新建一个工程用来显示我们的倒计时功能。如下图所示,我们先创建两个按钮,分别用来出发倒计时的事件。
倒计时按钮创建- 属性的设置
@interface ViewController ()
{
NSInteger _count;
}
@property(nonatomic,strong)UIButton *PushBtn;
@property(nonatomic,strong)UIButton *NSTimerbtn;
@property(nonatomic,strong)UIButton *GCDbtn;
/** 定时器(这里不用带*,因为dispatch_source_t就是个类,内部已经包含了*) */
@property (nonatomic, strong) dispatch_source_t timer;
- NSTimer 按钮点击事件中
self.NSTimerbtn.enabled =NO; //禁止点击
_count = 60;
[self.NSTimerbtn setTitle:@"60秒" forState:UIControlStateDisabled];
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
- 定时器事件
-(void)timerFired:(NSTimer *)timer
{
if (_count !=1) {
_count -=1;
[self.NSTimerbtn setTitle:[NSString stringWithFormat:@"%ld秒",_count] forState:UIControlStateDisabled];
}
else
{
[timer invalidate]; //定时器停止
self.NSTimerbtn.enabled = YES;
[self.NSTimerbtn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[self.NSTimerbtn setTitle:@"获取验证码" forState:UIControlStateNormal];
}
}
这样基本上就实现了简单的倒计时,运用的方法是NSTimer定时器。从上面的图中可以看到,还有一个GCD定时器的用法。
- GCD定时器实现倒计时
__block int count = 10;
// 获得队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 创建一个定时器
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 设置定时器的各种属性(几时开始任务,每隔多长时间执行一次)
// GCD的时间参数,一般是纳秒(1秒 == 10的9次方纳秒)
// 何时开始执行第一个任务
// dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC) 比当前时间晚3秒
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);
dispatch_source_set_timer(self.timer, start, interval, 0);
// 设置回调
dispatch_source_set_event_handler(self.timer, ^{
count = count - 1;
[self.GCDbtn setTitle:[NSString stringWithFormat:@"%d秒",count] forState:UIControlStateNormal];
self.GCDbtn.enabled = NO;
[self.GCDbtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
if (count == 0) {
// 取消定时器
dispatch_cancel(self.timer);
self.timer = nil;
self.GCDbtn.enabled = YES;
[self.GCDbtn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[self.GCDbtn setTitle:@"获取验证码" forState:UIControlStateNormal];
}
});
// 启动定时器
dispatch_resume(self.timer);
上述两种方法都可以实现倒计时的功能,如下图
倒计时实现真正的开始
我们是使用了NSTimer和GCD实现了倒计时,但是对它们的了解到底有多深呢。面对GCD和NSTimer我们要选择哪种方式呢。要想做出选择首先我们要对他们有一定的了解然后才能谁才是适合我们的。
如果选择GCD
如果说选择了GCD定时器,像上面那样写一个也就可以忍,但是如果用到多个定时器的话,每次都要写很多,实在太累了,就想封装一个,但是我写好之后,看到另一篇简书的内容,人家已经封装了,而且竟然比我封装的还全面(-_-`),这里就不放我自己的了,放个传送门吧(哈哈)GCD定时器封装的代码
具体的用法作者讲的比较清楚,这里在说一下,怎么用这个封装的定时器来实现倒计时验证码。首先在按钮的点击事件里,加入倒计时,然后实现倒计时里的方法。
[[JX_GCDTimerManager sharedInstance]cancelTimerWithName:@"mytimers"];
__weak typeof(self) weakSelf = self;
[[JX_GCDTimerManager sharedInstance]scheduledDispatchTimerWithName:@"mytimers" timeInterval:1 queue:nil
repeats:YES actionOption:AbandonPreviousAction action:^{
[weakSelf dosomthing];
下一步实现 [weakSelf dosomthing]方法,从而完成点击按钮显示数字。这里需要注意的就是UI的刷新必须回到主线程,因为计数是在子线程里可以完成的,但是UI的刷新如果不回到主线程是不会刷新的,你会误以为定时器无效,然而并不是。
-(void)dosomthing{
__weak typeof(self) weakSelf = self;
_count = _count - 1;
NSLog(@"%ld",_count);
dispatch_sync(dispatch_get_main_queue(), ^{
weakSelf.GCDbtn.enabled = NO;
[weakSelf.GCDbtn setTitle:[NSString stringWithFormat:@"%ld秒",_count] forState:UIControlStateNormal];
[weakSelf.GCDbtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
});
if (_count< 1) {
[[JX_GCDTimerManager sharedInstance] cancelTimerWithName:@"mytimers"];
weakSelf.GCDbtn.enabled = YES;
[weakSelf.GCDbtn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[weakSelf.GCDbtn setTitle:@"获取验证码" forState:UIControlStateNormal];
_count = 10;
}
}
这样我们就实现了用封装的GCD定时器实现的倒计时方法。自己把GCD定时器封装起来还是有很多好处的,用GCD定时器相对于NSTimer可以帮我们减少内存泄漏的风险,同时GCD还可以帮我们处理线程级的逻辑。有了封装的定时器我们就可以随心所欲的使用了啊。
如果选择NSTimer
NSTimer用起来也只是简单的使用,对他的认识也确实不够深入,这里就推荐一篇个人认为比较好的文章吧,反正我看了之后是学习良多,感慨iOS要学习的地方还很多啊。奉上博客原文:NSTimer你真的会用了吗
- NSTimer不是一种实时的机制,会存在延迟,而且延迟的程度跟当前线程的执行情况有关。会因为当前线程的堵塞导致延时。
- 必须得把timer添加到runloop中,它才会生效。
- 要让timer生效,必须保证该线程的runloop已启动,而且其运行的runloopmode也要匹配。
后记
关于定时器的这里就先说到这么多,有自己使用的也有自己学到的,感觉要学习的还很多,有许多博客和文章确实能学到许多东西,比如,文中举例的两篇文章,都讲解的比较深入,器这里权且当做是抛砖引玉吧。