GCD計時器替換NSTimer

而知無涯發表於2018-03-06

1.問題描述

使用NSTimer,repeats引數傳YES的時候,可能導致Timer不能及時釋放,如:

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                                  target:weakSelf
                                                selector:@selector(timerTick:)
                                                userInfo:nil
                                                 repeats:YES];
複製程式碼

此時,假如持有timer的是UIView物件self,self不能呼叫Dealloc釋放。所以,在需要釋放self的時候,需要先手動銷燬計時器timer。

- (void)dealloc {
    //即使將timer的target設定為weakSelf,也不會進入此方法,進行釋放
    [self destroyTimer];
}
複製程式碼

2.使用GCD計時器替換NSTimer可以解決以上問題

標頭檔案

#import <Foundation/Foundation.h>

@interface TTTimer : NSObject

/** 計時器總時長 */
@property (nonatomic, assign) NSInteger duration;
/** 鐘擺間隔 */
@property (nonatomic, assign) NSInteger tickInterval;

/**
 倒數計時
 */
- (void)tickDownProgress:(void(^)(void))progress;

- (void)tickDownCompletion:(void(^)(void))completion;

- (void)suspend;

- (void)resume;

- (void)destroy;

@end
複製程式碼

實現檔案

#import "TTTimer.h"

@interface TTTimer () {
    
    BOOL _isTickComplete;
    BOOL _isSuspend;
}

@property (nonatomic, strong) dispatch_source_t timer;

@end

@implementation TTTimer

- (instancetype)init {
    
    if (self = [super init]) {
        _duration = 0;
        _tickInterval = 0;
        _isTickComplete = NO;
        _isSuspend = YES;
    }
    
    return self;
}

- (void)tickDownProgress:(void(^)(void))progress {
    
    if(self.duration == 0) return;
    
    if (!self.timer) {
        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(self.timer,
                                  dispatch_walltime(NULL, 0),
                                  self.tickInterval * NSEC_PER_SEC,
                                  0);
        dispatch_source_set_event_handler(self.timer, ^{
            if (progress) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    progress();
                });
            }
        });
        
        [self resume];
    }
}

- (void)tickDownCompletion:(void(^)(void))completion {
    
    if(self.duration == 0) return;
    
    __block NSInteger timeOut = self.duration;
    if (!self.timer) {
        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(self.timer,
                                  dispatch_walltime(NULL, 0),
                                  self.tickInterval * NSEC_PER_SEC,
                                  0);
        dispatch_source_set_event_handler(self.timer, ^{
            if (timeOut <= 0) {
                [self destroy];
                if(completion) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        completion();
                    });
                }
                
                _isTickComplete = YES;
            }else {
                timeOut = timeOut - self.tickInterval;
            }
        });
        
        [self resume];
    }
}

- (void)suspend {
    
    if (self.timer && !_isSuspend) {
        dispatch_suspend(self.timer);
        _isSuspend = YES;
    }
}

- (void)resume {
    
    if (self.timer && _isSuspend) {
        dispatch_resume(self.timer);
        _isSuspend = NO;
    }
}

- (void)destroy {
    
    if (self.timer) {
        if (!_isTickComplete) {
            [self resume];
        }
        
        dispatch_source_cancel(self.timer);
        self.timer = nil;
    }
}

@end
複製程式碼
  1. 避免Block迴圈引用
- (void)setupTimer {
    
    __weak typeof(self) weakSelf = self;

    self.toolTimer = [[TTTimer alloc] init];
    self.toolTimer.tickInterval = 1;
    self.toolTimer.duration = 5;
    [self.toolTimer tickDownCompletion:^{
        [weakSelf doSomething];
    }];
}
複製程式碼

相關文章