鄭州iOS點 - 點哥講iOS的時間管理

大點哥發表於2017-03-23

最近在跟一個新專案...

整個專案只有不到20個介面,而且做的是即時通訊,socket協議有不到20個命令,前端邏輯重的不要不要的...

為了能完成需求文件上關於延時邏輯的任務,我把各種定時器都給用上了...

1. NSTimer;
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];


  

相關文章