最近在跟一个新项目...
整个项目只有不到20个接口,而且做的是即时通讯,socket协议有不到20个命令,前端逻辑重的不要不要的...
为了能完成需求文档上关于延时逻辑的任务,我把各种定时器都给用上了...
NSTimer是一种非常省事的定时器,但是好多猿都没躲过它的坑.
一号坑-----重复创建NSTimer;
有时候,我们在写方法的时候需要注意,避免重复创建,在设置属性了以后,多加个判断,像下面一样:
if (self.connectTimer == nil) {
self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(<##>) userInfo:@{<##>} repeats:YES];
[self.connectTimer fire];
}
而且建议我们尽量要将fire写在创建定时器方法外面:
- (void)timerFire {
if ([_connectTimer isValid]) {
[_connectTimer invalidate];
}
[self.connectTimer fire];
}
二号坑-----清除NSTimer;
清除和暂停是两回事,一旦再次调用,就会发现定时器会跑不止一遍,正确严谨的销毁NSTimer 的姿势应该是下面这样的:
- (void)shutDownTimer {
if ([_connectTimer isValid]) {
[_connectTimer invalidate];
}
_connectTimer =nil;
}
这样,我们就可以在任何地方创建,启动,销毁NSTimer了;
2.GCD;
GCD不用多说了吧,验证码倒计时最经典的使用方法了
__block int timeout = 60; //倒计时时间
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t _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, ^{
if(timeout<=0){ //倒计时结束,关闭
dispatch_source_cancel(_timer);
dispatch_async(dispatch_get_main_queue(), ^{
//设置界面的按钮显示 根据自己需求设置
[weakSelf.verificationCodeBtn setTitle:@"获取验证码" forState:UIControlStateNormal];
[weakSelf.verificationCodeBtn setTitleColor:WHITE_COLOR forState:(UIControlStateNormal)];
weakSelf.verificationCodeBtn.userInteractionEnabled = YES;
});
}else{
int seconds = timeout % 60;
NSString *strTime = [NSString stringWithFormat:@"%.2d", seconds];
dispatch_async(dispatch_get_main_queue(), ^{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1];
[weakSelf.verificationCodeBtn setTitle:[NSString stringWithFormat:@"%@秒后重新发送",strTime] forState:UIControlStateNormal];
[UIView commitAnimations];
weakSelf.verificationCodeBtn.userInteractionEnabled = NO;
});
timeout--;
}
});
dispatch_resume(_timer);
3.CADisplayLink
这个方法很有意思,思来想去,写在动画里是最完美的,这里分享一个单波纹的layer动画的定时器写法;
- (void)startAnimation {
CALayer *layer = [[CALayer alloc] init];
layer.cornerRadius = 15;
layer.frame = CGRectMake(0, 0, SCREEN_WIDTH, 30);
UIColor *color = BASIC_COLOR;
layer.backgroundColor = color.CGColor;
[self.aniView.layer addSublayer:layer];
CAMediaTimingFunction *defaultCurve = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
_animaTionGroup = [CAAnimationGroup animation];
_animaTionGroup.delegate = self;
_animaTionGroup.duration = 4;
_animaTionGroup.removedOnCompletion = YES;
_animaTionGroup.timingFunction = defaultCurve;
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale.xy"];
scaleAnimation.fromValue = @0.0;
scaleAnimation.toValue = @1.0;
scaleAnimation.duration = 4;
CAKeyframeAnimation *opencityAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
opencityAnimation.duration = 4;
opencityAnimation.values = @[@0.8,@0.4,@0];
opencityAnimation.keyTimes = @[@0,@0.5,@1];
opencityAnimation.removedOnCompletion = YES;
NSArray *animations = @[scaleAnimation,opencityAnimation];
_animaTionGroup.animations = animations;
[layer addAnimation:_animaTionGroup forKey:nil];
[self performSelector:@selector(removeLayer:) withObject:layer afterDelay:3];
}
- (void)removeLayer:(CALayer *)layer {
[layer removeFromSuperlayer];
}
- (void)start {
_disPlayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(delayAnimation)];
_disPlayLink.frameInterval = 300;
[_disPlayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)delayAnimation {
[self startAnimation];
[self updateTime];
}
- (void)stop {
[self.aniView.layer removeAllAnimations];
[_disPlayLink invalidate];
_disPlayLink = nil;
[_disPlayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
4.performSelector:@selector(<##>) withObject:<##> afterDelay:<##>;
这个更好理解了,在多少秒后实现指定的方法,注意,这个方法苹果是想废弃的,因为确实可能会有内存管理上的问题;
所以,我们可能要用上取消performSelector:方法的方法:
取消指定的延时:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(<##>) object:nil];
取消全部延时:
[NSObject cancelPreviousPerformRequestsWithTarget:self];