OC

内存管理-GCD定时器

Posted on 2022-01-04,3 min read

NSTimer依赖于RunLoop。如果RunLoop的任务过于繁重,可能会导致NSTimer不准时;
所以我们有时就需要更准时的GCD定时器

基本使用方式

    // 队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 创建定时器
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    // 设置时间
    NSTimeInterval start = 2.0; // 2秒后开始
    NSTimeInterval interval = 1.0; // 间隔
    dispatch_source_set_timer(timer,
                              dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC),
                              interval * NSEC_PER_SEC, 0);
    
    // 设置回调
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"currentThread %@" ,[NSThread currentThread]);
    });
    
    // 启动定时器
    dispatch_resume(timer);
    self.gcdTimer = timer; // 需要用强引用保留此定时器对象

自定义封装

  • MyGCDTimer.h
@interface MyGCDTimer : NSObject

/// block 形式开启 task 任务
/// @param task task事件
/// @param start 几秒后开始
/// @param interval 时间间隔
/// @param repeats 是否重复
/// @param async 异步 or 同步
+ (NSString *)doTask:(void(^)(void))task
               start:(NSTimeInterval)start
            interval:(NSTimeInterval)interval
             repeats:(BOOL)repeats
               async:(BOOL)async;

/// target seletor 形式开始 task任务
+ (NSString *)doTask:(id)target
            selector:(SEL)selector
               start:(NSTimeInterval)start
            interval:(NSTimeInterval)interval
             repeats:(BOOL)repeats
               async:(BOOL)async;

+ (void)cancelTask:(NSString *)taskName;

@end
  • MyGCDTimer.m
@implementation MyGCDTimer

static NSMutableDictionary *timers_; // timers 字典 {name : timer}
static dispatch_semaphore_t semaphore_; // 信号量保证线程安全

+ (void)initialize{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        timers_ = [NSMutableDictionary dictionary];
        semaphore_ = dispatch_semaphore_create(1);
    });
}

+ (NSString *)doTask:(void (^)(void))task start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async{
    if (!task || start < 0 || (interval <= 0 && repeats)) return nil;
    
    
    // 队列
    dispatch_queue_t queue = async ? dispatch_get_global_queue(0, 0) : dispatch_get_main_queue();
    
    // 创建定时器
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    // 设置时间
    dispatch_source_set_timer(timer,
                              dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC),
                              interval * NSEC_PER_SEC, 0);
    
    
    // 信号量保证线程安全
    dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
    // 定时器唯一标识
    NSString *name = [NSString stringWithFormat:@"%zd",timers_.count];
    // 存放到字典中(强引用)
    timers_[name] = timer;
    dispatch_semaphore_signal(semaphore_);
    
    // 设置回调
    dispatch_source_set_event_handler(timer, ^{
        task();
        if (!repeats) { // 不重复则取消
            [self cancelTask:name];
        }
    });
    
    // 启动定时器
    dispatch_resume(timer);
    
    return name;
}

+ (NSString *)doTask:(id)target selector:(SEL)selector start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async{
    if (!target || !selector) return nil;
    return [self doTask:^{
        if ([target respondsToSelector:selector]) {
            [target performSelector:selector withObject:nil];
        }
    } start:start interval:interval repeats:repeats async:async];
}

+ (void)cancelTask:(NSString *)taskName{
    if (taskName.length == 0) return;
    
    dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
    
    dispatch_source_t timer = timers_[taskName];
    if (timer) {
        dispatch_source_cancel(timer);
        [timers_ removeObjectForKey:taskName];
    }
    
    dispatch_semaphore_signal(semaphore_);
}

@end
  • 调用方式:
@property (nonatomic, strong) dispatch_source_t gcdTimer;
@property (nonatomic, copy) NSString *taskName;

- (void)viewDidLoad {
    [super viewDidLoad];
    self.taskName = [MyGCDTimer doTask:^{
        NSLog(@"currentThread %@" ,[NSThread currentThread]);
    } start:2.0 interval:1.0 repeats:YES async:YES];
}

- (void)dealloc{
    NSLog(@"%s",__func__);
    [MyGCDTimer cancelTask:_taskName];
}

下一篇: 内存管理-定时器→

loading...