美文网首页
CADisplayLink NSTimer NSProxy

CADisplayLink NSTimer NSProxy

作者: 曹来东 | 来源:发表于2018-09-14 16:51 被阅读3次

    CADisplayLink NSTimer

    • CADisplayLink、NSTimer会对target产生强引用,如果target又对它们产生强引用,那么就会引发循环引用
    • 解决方案 : 使用block

    下面的代码会造成循环引用,控制器和NSTimer, CADisplayLink对象都不会销毁.

    @interface ViewController ()
    @property (nonatomic,strong)  CADisplayLink * link;
    @property (nonatomic,strong)  NSTimer * timer;
    @end
    
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
     
        //保证调用频率和刷帧频率一致,60FPS
       self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkTest)];
        [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
    }
    - (void)timerTest{
        NSLog(@"%s",__func__);
    }
    - (void)linkTest{
        NSLog(@"%s",__func__);
    }
    
    -(void)dealloc{
        [self.link invalidate];
        [self.timer invalidate];
    }
    
    

    注意:下面的代码是不能解决循环引用的问题的!__weak弱引用是用来解决Block循环引用的问题,传入 weak指针Block内部会进行弱引用.并不适用解决我们当前的问题.因为无论传入的是强指针还是若指针都是控制器对象的地址.无法解决循环引用问题.

    __weak typeof(self) weakSelf = self;
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:weakSelf selector:@selector(timerTest) userInfo:nil repeats:YES];
    
    //使用Block才能使用weak解决循环引用问题.
       __weak typeof(self) weakSelf = self;
    
        self.timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
            [weakSelf timerTest];
        }];
    
    

    通过消息转发解决循环引用的问题

    image.png
    #import <Foundation/Foundation.h>
    
    @interface WeakDelegate : NSObject
    + (instancetype)initWithTarget:(id)target;
    @property (nonatomic,weak) id target;
    @end
    
    @implementation WeakDelegate
    + (instancetype)initWithTarget:(id)target{
        WeakDelegate * weakDelegate = [[WeakDelegate alloc] init];
        weakDelegate.target = target;
        return weakDelegate;
    }
    //消息转发
    - (id)forwardingTargetForSelector:(SEL)aSelector{
        return self.target;
    }
    @end
    
    //注意target参数
    - (void)viewDidLoad {
        [super viewDidLoad];
     
        //保证调用频率和刷帧频率一致,60FPS
       self.link = [CADisplayLink displayLinkWithTarget:[WeakDelegate initWithTarget:self] selector:@selector(linkTest)];
        [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
       
         self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[WeakDelegate initWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
    }
    
    //消息转发的三个方法
    - (id)forwardingTargetForSelector:(SEL)aSelector{}
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{}
    
    - (void)forwardInvocation:(NSInvocation *)anInvocation{}
    

    NSProxy

    @interface LDProxy : NSProxy
    + (instancetype)proxyWithTarget:(id)target;
    @property (nonatomic,weak) id target;
    @end
    
    #import "LDProxy.h"
    
    @implementation LDProxy
    + (instancetype)proxyWithTarget:(id)target{
        LDProxy * proxy = [LDProxy alloc];
        proxy.target = target;
        return proxy;
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
        return [self.target methodSignatureForSelector:sel];
    }
    
    - (void)forwardInvocation:(NSInvocation *)invocation{
        invocation.target = self.target;
        [invocation invokeWithTarget:self.target];
    }
    @end
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        //保证调用频率和刷帧频率一致,60FPS
       self.link = [CADisplayLink displayLinkWithTarget:[LDProxy proxyWithTarget:self] selector:@selector(linkTest)];
        [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
     
         self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[LDProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
     
    }
    

    GCD定时器

    • NSTimer依赖于RunLoop,如果RunLoop的任务过于繁重,可能会导致NSTimer不准时
    • 而GCD的定时器会更加准时
    //队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        //创建定时器
        dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        
        //设置时间
        uint64_t start = 2.0;//2秒后开始执行
        uint64_t interval = 1.0;//每隔1秒执行
        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(@"111");
        });
        //启动定时器
        dispatch_resume(timer);
    
    
    //通过传入函数方式执行定时器任务
    - (void)viewDidLoad {
        [super viewDidLoad];
    
       //队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        //创建定时器
        dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        
        //设置时间
        uint64_t start = 2.0;//2秒后开始执行
        uint64_t interval = 1.0;//每隔1秒执行
        dispatch_source_set_timer(timer,
                                  dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC)
                                  , interval * NSEC_PER_SEC, 0);
        
        //设置回调,注意这个方法名多了一个_f
        dispatch_source_set_event_handler_f(timer, timerFire);
        //启动定时器
        dispatch_resume(timer);
        
    }
    void timerFire(void * param){
        
    }
    
    • GCD的定时器不依赖与RunLoop,非常准时.而且GCD的内存管理也不需要程序员管理,是GCD内部自己维护的.

    GCD定时器封装

    
    #import <Foundation/Foundation.h>
    
    @interface LDTimer : NSObject
    + (NSString *)execTask:(void(^)(void))task
               start:(NSTimeInterval)start
            interval:(NSTimeInterval)interval
             repeats:(BOOL)repeats
               async:(BOOL)async;
    
    + (NSString *)execTask:(id)target
                  selector:(SEL)selector
                     start:(NSTimeInterval)start
                  interval:(NSTimeInterval)interval
                   repeats:(BOOL)repeats
                     async:(BOOL)async;
    
    + (void)cancelTask:(NSString *)name;
    @end
    
    #import "LDTimer.h"
    
    
    @implementation LDTimer
    
    static NSMutableDictionary * timers_;
    
    dispatch_semaphore_t semaphore_;;
    
    + (void)initialize{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            timers_ = [NSMutableDictionary dictionary];
            semaphore_ = dispatch_semaphore_create(1);
        });
        
    }
    
    + (NSString *)execTask:(id)target
                  selector:(SEL)selector
                     start:(NSTimeInterval)start
                  interval:(NSTimeInterval)interval
                   repeats:(BOOL)repeats
                     async:(BOOL)async{
        if (!target || !selector) return nil;
        
        return [self execTask:^{
            if ([target respondsToSelector:selector]) {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
                [target performSelector:selector];
    #pragma clang diagnostic pop
            }
        } start:start interval:interval repeats:repeats async:async];
    }
    + (NSString *)execTask:(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:@"%lu",(unsigned long)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;
    }
    + (void)cancelTask:(NSString *)name{
        if (name.length == 0) return;
        
        dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
        
        dispatch_source_t timer = timers_[name];
        if (timer) {
            dispatch_source_cancel(timer);
            [timers_ removeObjectForKey:name];
        }
        dispatch_semaphore_signal(semaphore_);
    }
    @end
    //接口设计 调用.
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //两种调用方式
      self.task = [LDTimer execTask:^{
          
          NSLog(@"LDTimer execTask");
          
        } start:2.0 interval:1.0 repeats:YES async:YES];
        
     
        self.task = [LDTimer execTask:self selector:@selector(doTask) start:1.0 interval:2.0 repeats:YES async:NO];
    }
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        
        [LDTimer cancelTask:self.task];
    }
    
    
    - (void)doTask{
        NSLog(@"LDTimer doTask");
    
    }
    
    

    相关文章

      网友评论

          本文标题:CADisplayLink NSTimer NSProxy

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