欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

GCD定时器封装OC&Swift

程序员文章站 2024-01-14 22:33:16
...

前言

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

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:812157648,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

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