定时器dispatch_timer的简单封装

977 阅读2分钟

之前看到别的同事写过的一个定时器的方法 , 最近项目中用到了这个定时器 , 试着照抄封装起来在这边记录一下封装的过程:

//
//  MDTimer.h
//  定时器

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

// 创建定时器类:

@interface MDTimer : NSObject

+ (NSString *)timerWithTaskBlock:(void(^)(void))task start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async;

+ (NSString *)timerWithTaskTarget:(id)target selector:(SEL)selector start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async;

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

@end

NS_ASSUME_NONNULL_END

//
//  MDTimer.m
//  定时器

#import "MDTimer.h"

@interface MDTimer ()

@property(nonatomic, strong) dispatch_source_t timer;

@end

@implementation MDTimer

static NSMutableDictionary *timers_;
static dispatch_semaphore_t semaphore_;

+ (void)initialize {
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        timers_ = [NSMutableDictionary dictionary];
        // 同一时间 , 创建任务与取消任务只能有一个事务在执行:
        semaphore_ = dispatch_semaphore_create(1);
    });
}


/// 通过Block方法执行定时器任务:
/// @param task 任务
/// @param start 开始时间
/// @param interval 任务时间间隔
/// @param repeats 是否重复
/// @param async 是否异步执行任务
/// @return 返回本次任务的一个标识
+ (NSString *)timerWithTaskBlock:(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 mainQueue = dispatch_get_main_queue();
    // 获取自定义一个串行队列:
    dispatch_queue_t serialQueue = dispatch_queue_create("timer", DISPATCH_QUEUE_SERIAL);
    // 根据外部传递进来的是否异步执行定制当执行前任务所在的队列是哪个:
    dispatch_queue_t queue = async ? serialQueue : mainQueue;
    // 定制定时器:
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    // 设置时间:
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, interval * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    
    /**
     *  对Map的操作同步加锁解锁:
     *
     */
    // 加锁:
    dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
    // 用Map中的任务个数作为定时器的唯一标识:
    NSString *identifier = [NSString stringWithFormat:@"%zd" , timers_.count];
    // 把定时器任务存储到Map中保存:
    timers_[identifier] = timer;
    // 解锁:
    dispatch_semaphore_signal(semaphore_);
    
    // 执行任务回调:
    dispatch_source_set_event_handler(timer, ^{
        // 执行任务:
        task();
        // 如果没有重复的任务那就只执行上面一次 , 然后就取消该任务:
        if (!repeats) [self cancelTask:identifier];
    });
    // 开启定时器任务:
    dispatch_resume(timer);
    
    return identifier;
}


/// 通过Target-Selector方法执行定时器任务:
/// @param target 目标对象
/// @param selector 方法选择器
/// @param start 开始时间
/// @param interval 任务时间间隔
/// @param repeats 是否重复
/// @param async 是否异步执行任务
/// @return 返回本次任务的一个标识
+ (NSString *)timerWithTaskTarget:(id)target selector:(SEL)selector start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async {
    if (!target || !selector) return nil;
    return [self timerWithTaskBlock:^{
        if ([target respondsToSelector:selector]) [target performSelector:selector];
    } start:start interval:interval repeats:repeats async:async];
}


/// 取消任务
/// @param identifier 唯一标识
+ (void)cancelTask:(NSString *)identifier {
    if (!timers_ || timers_.count == 0) return;
    // 加锁:
    dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
    // 取消定时器任务:
    dispatch_source_cancel(timers_[identifier]);
    // 同时将定时器从Map中也一并移除:
    [timers_ removeObjectForKey:identifier];
    // 任务执行完成后解锁:
    dispatch_semaphore_signal(semaphore_);
}

@end