美文网首页
GCD定时器

GCD定时器

作者: 小心韩国人 | 来源:发表于2019-12-17 18:50 被阅读0次

    我们上次在内存管理一:NSTimer讲了NSTimerCADisplayLink的循环引用的问题.实际上这两个计时器并不是一定准时的,因为他们都依赖于runloop,如果runloop中有耗时的操作,那么定时器事件的调用就会受到影响.
    所以如果想让定时器准确的执行任务,最好使用GCD的定时器.

    @interface ViewController ()
    
    @property (nonatomic,strong)dispatch_source_t timer ;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        
        //创建一个定时器
        self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
        //设置事件
        dispatch_source_set_timer(
                                  self.timer,
                                  dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC),//开始时间,从 3 秒后开始
                                  1.0 * NSEC_PER_SEC,//每间隔 1 秒
                                  0
                                  );
        //设置定时器回调,采用block的方式
    //    dispatch_source_set_event_handler(self.timer, ^{
    //        NSLog(@"定时器事件");
    //    });
        
    //设置定时器回调,采用 函数方式 _f 是function
        dispatch_source_set_event_handler_f(self.timer, timerAction);
        
        //启动定时器
        dispatch_resume(self.timer);
    }
    
    
    void timerAction(void *para){
        NSLog(@"定时器事件");
    }
    
    @end
    
    

    这样我们就创建了一个GCD的定时器,并且成功的运行了.即使往ViewController中添加一个textView,滚动textView仍然不会影响即使工作,因为GCD的计时器并不依赖与runloop,他是直接和系统内核挂钩的.
    下面我们就来封装一个GCD的定时器,方便以后使用:

    @interface GoodTimer : NSObject
    
    //添加任务,创建定时器后会返回定时器的唯一标识
    + (NSString *)executeTask:(void(^)(void))task
                start:(NSTimeInterval)start
                interval:(NSTimeInterval)interval
                repeats:(BOOL)repeats
                async:(BOOL)async;
    
    
    //添加任务,采用target sel 的方式
    + (NSString *)executeTaskWithTarget:(id)target
                selector:(SEL)sel
                start:(NSTimeInterval)start
                interval:(NSTimeInterval)interval
                repeats:(BOOL)repeats
                async:(BOOL)async;
    
    
    
    //根据唯一标识,停止任务
    + (void)cancelTask:(NSString *)timerIdentifier;
    @end
    
    
    ------------------------------------------------------------------------------
    
    
    #import "GoodTimer.h"
    
    static NSMutableDictionary *timerDic_;
    /*因为创建任务和取消任务都会访问timerDic_
     ,如果是多线程的话,很可能出现问题,
     所以要做加锁解锁操作*/
    static dispatch_semaphore_t semaphore_;
    
    @implementation GoodTimer
    
    //initialize会在这个类第一次接受消息的时候调用
    + (void)initialize{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            timerDic_ = [NSMutableDictionary dictionary];
            semaphore_ = dispatch_semaphore_create(1);
        });
    }
    
    + (NSString *)executeTask:(void (^)(void))task start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async{
        
        if (!task || start < 0 || (repeats && interval <= 0)) {
            return nil;
        }
        
        //根据 async 决定是主线程还是子线程
        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);
    #pragma mark 涉及到字典读取和写入的操作需要加锁,解锁
        //加锁
        dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
        //获取identifier
        NSString *identifier = [NSString stringWithFormat:@"%zd",[timerDic_ count]];
        //把定时器加入到timerDic_
        timerDic_[identifier] = timer;
        //解锁
        dispatch_semaphore_signal(semaphore_);
        //设置回调
        dispatch_source_set_event_handler(timer, ^{
            task();
            if (!repeats) {
                //不重复,就取消定时器
                [self cancelTask:identifier];
            }
           });
        //启动定时器
        dispatch_resume(timer);
        return identifier;
    }
    
    
    + (NSString *)executeTaskWithTarget:(id)target selector:(SEL)sel start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async{
        return [self executeTask:^{
            if ([target respondsToSelector:sel]) {
                #pragma clang diagnostic push
                #pragma clang diagnostic ignored"-Warc-performSelector-leaks"
                [target performSelector:sel];
                #pragma clang diagnostic pop
            }
        } start:start interval:interval repeats:repeats async:async];
    }
    
    
    //取消任务
    + (void)cancelTask:(NSString *)timerIdentifier{
        if (timerIdentifier.length == 0) {
            return;
        }
        //加锁
        dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
        dispatch_source_t timer = timerDic_[timerIdentifier];
        if (timer) {
            //取消任务
            dispatch_source_cancel(timerDic_[timerIdentifier]);
            //把任务从timerDic_中移除
            [timerDic_ removeObjectForKey:timerIdentifier];
        }
        //解锁
        dispatch_semaphore_signal(semaphore_);
    }
    @end
    
    

    源码在这里

    相关文章

      网友评论

          本文标题:GCD定时器

          本文链接:https://www.haomeiwen.com/subject/untenctx.html