GCD定时器封装OC&Swift

前言

iOS开发中,经常需要使用到定时器,使用NSTimer很容易出现内存泄漏,在此简单封装GCD定时器。

OC版本

@interface CLGCDTimer ()

/**队列*/
@property (nonatomic, strong) dispatch_queue_t queue;
/**定时器*/
@property (nonatomic, strong) dispatch_source_t timer;
/**执行时间*/
@property (nonatomic, assign) NSTimeInterval interval;
/**延迟时间*/
@property (nonatomic, assign) NSTimeInterval delaySecs;
/**是否重复*/
@property (nonatomic, assign) BOOL repeat;
/**是否正在运行*/
@property (nonatomic, assign) BOOL isRuning;
/**响应次数*/
@property (nonatomic, assign) NSInteger actionTimes;
///名称
@property (nonatomic, copy) NSString *name;
/**响应*/
@property (nonatomic, copy) void (^action) (NSInteger actionTimes);

@end

@implementation CLGCDTimer

- (instancetype _Nonnull)initWithName:(NSString *)name
                             interval:(NSTimeInterval)interval
                            delaySecs:(NSTimeInterval)delaySecs
                                queue:(dispatch_queue_t _Nullable)queue
                              repeats:(BOOL)repeats
                               action:(void(^ _Nullable)(NSInteger actionTimes))action {
    CLGCDTimer *timer = [self initWithInterval:interval
                                     delaySecs:delaySecs
                                         queue:queue
                                       repeats:repeats
                                        action:action];
    timer.name = name;
    return timer;
}
- (instancetype _Nonnull)initWithInterval:(NSTimeInterval)interval
                                delaySecs:(NSTimeInterval)delaySecs
                                    queue:(dispatch_queue_t _Nullable)queue
                                  repeats:(BOOL)repeats
                                   action:( void(^ _Nullable)(NSInteger actionTimes))action {
    if (self = [super init]) {
        if (queue == nil) {
            queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        }
        self.interval = interval;
        self.delaySecs = delaySecs;
        self.repeat = repeats;
        self.action = action;
        self.isRuning = NO;
        self.queue = dispatch_queue_create([[NSString stringWithFormat:@"CLGCDTimer.%p", self] cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL);
        self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue);
        dispatch_set_target_queue(self.queue, queue);
    }
    return self;
}
- (void)replaceOldAction:(void(^ _Nonnull)(NSInteger actionTimes))action {
    if (action) {
        self.action = action;
    }
}
/**开始定时器*/
- (void)start {
    dispatch_source_set_timer(self.timer, dispatch_time(DISPATCH_TIME_NOW, (NSInteger)(self.delaySecs * NSEC_PER_SEC)),(NSInteger)(self.interval * NSEC_PER_SEC), 0 * NSEC_PER_SEC);
    __weak __typeof(self) weakSelf = self;
    dispatch_source_set_event_handler(self.timer, ^{
        __typeof(&*weakSelf) strongSelf = weakSelf;
        strongSelf.actionTimes ++;
        if (strongSelf.action) {
            strongSelf.action(strongSelf.actionTimes);
        }
        if (!strongSelf.repeat) {
            [strongSelf cancel];
        }
    });
    [self resume];
}
/**执行一次定时器响应*/
- (void)responseOnce {
    self.actionTimes ++;
    self.isRuning = YES;
    if (self.action) {
        self.action(self.actionTimes);
    }
    self.isRuning = NO;
}
/**取消定时器*/
- (void)cancel {
    if (!self.isRuning) {
        [self resume];
    }
    dispatch_source_cancel(self.timer);
}
/**暂停定时器*/
- (void)suspend {
    if (self.isRuning) {
        dispatch_suspend(self.timer);
        self.isRuning = NO;
    }
}
/**恢复定时器*/
- (void)resume {
    if (!self.isRuning) {
        dispatch_resume(self.timer);
        self.isRuning = YES;
    }
}
-(void)dealloc {
    [self cancel];
}

@end

@interface CLGCDTimerManager ()

/**CLGCDTimer字典*/
@property (nonatomic, strong) NSMutableDictionary *timerObjectCache;
/**信号*/
@property (nonatomic, strong) dispatch_semaphore_t semaphore;

@end

@implementation CLGCDTimerManager

#pragma mark - 初始化
//第1步: 存储唯一实例
static CLGCDTimerManager *_manager = nil;
//第2步: 分配内存空间时都会调用这个方法. 保证分配内存alloc时都相同.
+ (id)allocWithZone:(struct _NSZone *)zone {
    //调用dispatch_once保证在多线程中也只被实例化一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _manager = [super allocWithZone:zone];
    });
    return _manager;
}
//第3步: 保证init初始化时都相同
+ (instancetype)sharedManager {
    return [[self alloc] init];
}
- (id)init {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _manager = [super init];
        self.timerObjectCache = [NSMutableDictionary dictionary];
        self.semaphore = dispatch_semaphore_create(0);
        dispatch_semaphore_signal(self.semaphore);
    });
    return _manager;
}
//第4步: 保证copy时都相同
- (id)copyWithZone:(NSZone __unused*)zone {
    return _manager;
}
//第五步: 保证mutableCopy时相同
- (id)mutableCopyWithZone:(NSZone __unused*)zone {
    return _manager;
}
//MARK:JmoVxia---创建定时器
+ (void)scheduledTimerWithName:( NSString * _Nonnull)name
                      interval:(NSTimeInterval)interval
                     delaySecs:(NSTimeInterval)delaySecs
                         queue:(dispatch_queue_t _Nullable)queue
                       repeats:(BOOL)repeats
                        action:(void(^ _Nullable)(NSInteger actionTimes))action {
    __strong NSString *string = name;
    CLGCDTimer *GCDTimer = [self timer:string];
    if (GCDTimer) {
        return;
    }
    GCDTimer = [[CLGCDTimer alloc] initWithName:string
                                       interval:interval
                                      delaySecs:delaySecs
                                          queue:queue
                                        repeats:repeats
                                         action:action];
    [self setTimer:GCDTimer name:string];
}
//MARK:JmoVxia---开始定时器
+ (void)start:(NSString *_Nonnull)name {
    __strong NSString *string = name;
    CLGCDTimer *GCDTimer = [self timer:string];
    if (GCDTimer) {
        [GCDTimer start];
    }
}
//MARK:JmoVxia---执行一次定时器响应
+ (void)responseOnce:(NSString *_Nonnull)name {
    __strong NSString *string = name;
    CLGCDTimer *GCDTimer = [self timer:string];
    if (GCDTimer) {
        [GCDTimer responseOnce];
    }
}
//MARK:JmoVxia---取消定时器
+ (void)cancel:(NSString *_Nonnull)name {
    __strong NSString *string = name;
    CLGCDTimer *GCDTimer = [self timer:string];
    if (GCDTimer) {
        [GCDTimer cancel];
        [self removeTimer:name];
    }
}
//MARK:JmoVxia---暂停定时器
+ (void)suspend:(NSString *_Nonnull)name {
    __strong NSString *string = name;
    CLGCDTimer *GCDTimer = [self timer:string];
    if (GCDTimer) {
        [GCDTimer suspend];
    }
}
//MARK:JmoVxia---恢复定时器
+ (void)resume:(NSString *_Nonnull)name {
    __strong NSString *string = name;
    CLGCDTimer *GCDTimer = [self timer:string];
    if (GCDTimer) {
        [GCDTimer resume];
    }
}
//MARK:JmoVxia---获取定时器
+ (CLGCDTimer *_Nullable)timer:(NSString *_Nonnull)name {
    dispatch_semaphore_wait([CLGCDTimerManager sharedManager].semaphore, DISPATCH_TIME_FOREVER);
    __strong NSString *string = name;
    CLGCDTimer *GCDTimer = [[CLGCDTimerManager sharedManager].timerObjectCache objectForKey:string];
    dispatch_semaphore_signal([CLGCDTimerManager sharedManager].semaphore);
    return GCDTimer;
}
//MARK:JmoVxia---储存定时器
+ (void)setTimer:(CLGCDTimer *_Nonnull)timer name:(NSString *_Nonnull)name {
    dispatch_semaphore_wait([CLGCDTimerManager sharedManager].semaphore, DISPATCH_TIME_FOREVER);
    [[CLGCDTimerManager sharedManager].timerObjectCache setObject:timer forKey:name];
    dispatch_semaphore_signal([CLGCDTimerManager sharedManager].semaphore);
}
//MARK:JmoVxia---移除定时器
+ (void)removeTimer:(NSString *_Nonnull)name {
    dispatch_semaphore_wait([CLGCDTimerManager sharedManager].semaphore, DISPATCH_TIME_FOREVER);
    [[CLGCDTimerManager sharedManager].timerObjectCache removeObjectForKey:name];
    dispatch_semaphore_signal([CLGCDTimerManager sharedManager].semaphore);
}
@end

Swift版本

class CLGCDTimer: NSObject {
    
    typealias actionBlock = ((NSInteger) -> Void)

    ///执行时间
    private var interval: TimeInterval!
    ///延迟时间
    private var delaySecs: TimeInterval!
    ///队列
    private var serialQueue: DispatchQueue!
    ///是否重复
    private var repeats: Bool = true
    ///响应
    private var action: actionBlock?
    ///定时器
    private var timer: DispatchSourceTimer!
    ///是否正在运行
    private var isRuning: Bool = false
    ///响应次数
    private (set) var actionTimes: NSInteger = 0
    
    /// 创建定时器
    ///
    /// - Parameters:
    ///   - interval: 间隔时间
    ///   - delaySecs: 第一次执行延迟时间,默认为0
    ///   - queue: 定时器调用的队列,默认子队列
    ///   - repeats: 是否重复执行,默认true
    ///   - action: 响应
    init(interval: TimeInterval, delaySecs: TimeInterval = 0, queue: DispatchQueue = DispatchQueue.global(), repeats: Bool = true, action:  actionBlock?) {
        super.init()
        self.interval = interval
        self.delaySecs = delaySecs
        self.serialQueue = DispatchQueue.init(label: String(format: "CLGCDTimer.%p", self), target: queue)
        self.action = action
        self.timer = DispatchSource.makeTimerSource(queue: self.serialQueue)
    }
    ///替换旧响应
    func replaceOldAction(action: actionBlock?) -> Void {
        guard let action = action else {
            return
        }
        self.action = action
    }
    ///执行一次定时器响应
    func responseOnce() {
        actionTimes += 1
        isRuning = true
        action?(actionTimes)
        isRuning = false
    }
    deinit {
        cancel()
    }
}

extension CLGCDTimer {
    ///开始定时器
    func start() {
        timer.schedule(deadline: .now() + delaySecs, repeating: interval, leeway: DispatchTimeInterval.never)
        timer.setEventHandler { [weak self] in
            guard let strongSelf = self else {
                return
            }
            strongSelf.actionTimes += 1
            strongSelf.action?(strongSelf.actionTimes)
            if !strongSelf.repeats {
                strongSelf.cancel()
            }
        }
        resume()
    }
    ///暂停
    func suspend() {
        if isRuning {
            timer.suspend()
            isRuning = false
        }
    }
    ///恢复定时器
    func resume() {
        if !isRuning {
            timer.resume()
            isRuning = true;
        }
    }
    ///取消定时器
    func cancel() {
        if !isRuning {
            resume()
        }
        timer.cancel()
    }
}

class CLGCDTimerManager: NSObject {
    
    typealias actionBlock = ((NSInteger) -> Void)
    
    private static let sharedManager: CLGCDTimerManager = CLGCDTimerManager()
    private var timerObjectCache: Dictionary<String, CLGCDTimer> = Dictionary()
    private let semaphore: DispatchSemaphore = DispatchSemaphore(value: 0)
    private override init() {
       semaphore.signal()
    }
    
    /// 创建全局定时器
    ///
    /// - Parameters:
    ///   - name: 定时器名称
    ///   - interval: 间隔时间
    ///   - delaySecs: 第一次执行延迟时间,默认为0
    ///   - queue: 定时器调用的队列,默认子队列
    ///   - repeats: 是否重复执行,默认true
    ///   - action: 响应
    class func scheduledTimer(name: String, interval: TimeInterval, delaySecs: TimeInterval = 0, queue: DispatchQueue = DispatchQueue.global(), repeats: Bool = true, action:  actionBlock?) {
        var GCDTimer: CLGCDTimer? = timer(name)
        if (GCDTimer != nil) {
            return
        }
        GCDTimer = CLGCDTimer(interval: interval, delaySecs: delaySecs, queue: queue, repeats: repeats, action: action)
        setTimer(GCDTimer!, name)
    }
}
extension CLGCDTimerManager {
    ///开始定时器
    class func start(_ name: String) {
        let GCDTimer: CLGCDTimer? = timer(name)
        GCDTimer?.start()
    }
    ///执行一次定时器
    class func responseOnce(_ name: String) {
        let GCDTimer: CLGCDTimer? = timer(name)
        GCDTimer?.responseOnce()
    }
    ///取消定时器
    class func cancel(_ name: String) {
        let GCDTimer: CLGCDTimer? = timer(name)
        guard let timer = GCDTimer else {
            return
        }
        timer.cancel()
        removeTimer(name)
    }
    ///暂停定时器
    class func suspend(_ name: String) {
        let GCDTimer: CLGCDTimer? = timer(name)
        GCDTimer?.suspend()
    }
    ///恢复定时器
    class func resume(_ name: String) {
        let GCDTimer: CLGCDTimer? = timer(name)
        GCDTimer?.resume()
    }
}
extension CLGCDTimerManager {
    private class func timer(_ name: String) -> CLGCDTimer? {
        sharedManager.semaphore.wait()
        let GCDTimer = sharedManager.timerObjectCache[name]
        sharedManager.semaphore.signal()
        return GCDTimer
    }
    private class func setTimer(_ timer: CLGCDTimer, _ name: String) {
        sharedManager.semaphore.wait()
        sharedManager.timerObjectCache.updateValue(timer, forKey: name)
        sharedManager.semaphore.signal()
    }
    private class func removeTimer(_ name: String) {
        sharedManager.semaphore.wait()
        sharedManager.timerObjectCache.removeValue(forKey: name)
        sharedManager.semaphore.signal()
    }
}

其他

简单的封装了一下,仅供大家参考,代码比较简单,完整项目地址——>CLDemo

    原文作者:季末微夏
    原文地址: https://www.jianshu.com/p/6ce61ccca31a
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞