美文网首页ios
NSTimer、CADisplayLink、GCD定时器

NSTimer、CADisplayLink、GCD定时器

作者: 紫荆秋雪_文 | 来源:发表于2018-07-25 23:35 被阅读30次

    一、NSTimer

    • NSTimer和CADisplayLink依赖于RunLoop,如果RunLoop的任务过于繁重,可能会导致NSTimer不准时,相比之下GCD的定时器会更加准时,因为GCD不是依赖RunLoop,而是由内核决定
    • CADisplayLink和NSTimer会对target产生强引用,如果target又对它们产生强引用,那么就会引发循环引用

    测试NSTimer的工程介绍

    点击NSTimerBtn跳转到最左边控制器,NSTimer开始工作;点击Back时定时器停止工作

    NSTimer工程介绍.png

    实例一

    • 1、使用的API
    /**
       已经加入到runloop中的定时器
    
       @param ti 间隔时间
       @param aTarget 执行任务的target
       @param aSelector 执行任务方法
       @param userInfo 参数
       @param yesOrNo 是否重复
       @return 定时器
     */
    + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
    
    • 2、测试代码
    #import "ViewController.h"
    
    @interface ViewController ()
    
    /** NSTimer */
    @property (nonatomic, strong) NSTimer *timer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFun:) userInfo:@"参数" repeats:YES];
    }
    
    - (void)timerFun:(NSTimer *)timer {
        
        NSLog(@"%s --%@--%@", __func__, [NSThread currentThread], timer.userInfo);
        
    }
    
    - (void)dealloc {
        NSLog(@"%s", __func__);
        [self.timer invalidate];
        self.timer = nil;
    }
    
    @end
    
    • 3、打印输出
    2018-07-25 11:00:53.487606+0800 NSTimer[2046:70846] -[ViewController timerFun:] --<NSThread: 0x60000006bb00>{number = 1, name = main}--参数
    2018-07-25 11:00:54.488425+0800 NSTimer[2046:70846] -[ViewController timerFun:] --<NSThread: 0x60000006bb00>{number = 1, name = main}--参数
    2018-07-25 11:00:55.487701+0800 NSTimer[2046:70846] -[ViewController timerFun:] --<NSThread: 0x60000006bb00>{number = 1, name = main}--参数
    2018-07-25 11:00:56.487452+0800 NSTimer[2046:70846] -[ViewController timerFun:] --<NSThread: 0x60000006bb00>{number = 1, name = main}--参数
    
    • 问题
      • 当点击Back时,定时器并没有停止工作,而且ViewController也没有销毁,NSTimer造成了循环引用 NSTimer_target循环引用.png
    • 分析
      • 要想解除循环引用,必须让上图中的一个引用是弱引用
      • 在解决block的循环引用时我们使用__weak修饰的self,那在NSTimer的target循环引用可不可以使用__weak来解决循环引用呢

    1、使用__weak解决循环引用(解决不了NSTimer的循环引用)

    • 1、测试代码
    #import "ViewController.h"
    
    @interface ViewController ()
    
    /** NSTimer */
    @property (nonatomic, strong) NSTimer *timer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        __weak typeof(self)weakSelf = self;
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:weakSelf selector:@selector(timerFun:) userInfo:@"参数" repeats:YES];
    }
    
    - (void)timerFun:(NSTimer *)timer {
        
        NSLog(@"%s --%@--%@", __func__, [NSThread currentThread], timer.userInfo);
        
    }
    
    - (void)dealloc {
        NSLog(@"%s", __func__);
        [self.timer invalidate];
        self.timer = nil;
    }
    @end
    
    • 打印输出
    2018-07-25 11:21:00.788221+0800 NSTimer[2325:86420] -[ViewController timerFun:] --<NSThread: 0x60000007edc0>{number = 1, name = main}--参数
    2018-07-25 11:21:01.788937+0800 NSTimer[2325:86420] -[ViewController timerFun:] --<NSThread: 0x60000007edc0>{number = 1, name = main}--参数
    2018-07-25 11:21:02.788234+0800 NSTimer[2325:86420] -[ViewController timerFun:] --<NSThread: 0x60000007edc0>{number = 1, name = main}--参数
    2018-07-25 11:21:03.787947+0800 NSTimer[2325:86420] -[ViewController timerFun:] --<NSThread: 0x60000007edc0>{number = 1, name = main}--参数
    2018-07-25 11:21:04.788652+0800 NSTimer[2325:86420] -[ViewController timerFun:] --<NSThread: 0x60000007edc0>{number = 1, name = main}--参数
    2018-07-25 11:21:05.787843+0800 NSTimer[2325:86420] -[ViewController timerFun:] --<NSThread: 0x60000007edc0>{number = 1, name = main}--参数
    
    • 使用__weak是没有办法解决NSTimer的target循环引用
    • NSTimer内部有一个属性来保存外界传入的target的,所以无论外界传入的target是weakSelf还是self,对于NSTimer内部来说都会强引用你传入的target

    2、使用消息转发来解决循环引用

    可以创建一个中间对象来充当这个target,由于上面的分析,NSTimer是一定会强引用这个target,也就是中间件,所以只能使用中间件弱引用来指向self来解决NSTimer的target循环引用

    • 测试代码
    • 1、中间件RevanProxy
    #import <Foundation/Foundation.h>
    
    @interface RevanProxy : NSObject
    
    /**
     构造中间件
     */
    +(instancetype)revan_proxy:(id)target;
    
    @end
    
    #import "RevanProxy.h"
    
    @interface RevanProxy ()
    
    /** target */
    @property (nonatomic, weak) id target;
    
    @end
    
    @implementation RevanProxy
    
    /**
     构造中间件
     */
    +(instancetype)revan_proxy:(id)target {
        RevanProxy *proxy = [[RevanProxy alloc] init];
        proxy.target = target;
        return proxy;
    }
    
    
    /**
     消息转发
    
     @param aSelector 消息
     @return 执行消息的对象
     */
    - (id)forwardingTargetForSelector:(SEL)aSelector {
        return self.target;
    }
    
    @end
    
    • 2、测试代码
    #import "ViewController.h"
    #import "RevanProxy.h"
    
    @interface ViewController ()
    
    /** NSTimer */
    @property (nonatomic, strong) NSTimer *timer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[RevanProxy revan_proxy:self] selector:@selector(timerFun:) userInfo:@"参数" repeats:YES];
    }
    
    - (void)timerFun:(NSTimer *)timer {
        
        NSLog(@"%s --%@--%@", __func__, [NSThread currentThread], timer.userInfo);
        
    }
    
    - (void)dealloc {
        NSLog(@"%s", __func__);
        [self.timer invalidate];
        self.timer = nil;
    }
    
    @end
    
    • 3、打印输出
    2018-07-25 11:39:02.230742+0800 NSTimer[2581:99832] -[ViewController timerFun:] --<NSThread: 0x6000002612c0>{number = 1, name = main}--参数
    2018-07-25 11:39:03.231457+0800 NSTimer[2581:99832] -[ViewController timerFun:] --<NSThread: 0x6000002612c0>{number = 1, name = main}--参数
    2018-07-25 11:39:04.230871+0800 NSTimer[2581:99832] -[ViewController timerFun:] --<NSThread: 0x6000002612c0>{number = 1, name = main}--参数
    2018-07-25 11:39:04.558871+0800 NSTimer[2581:99832] -[ViewController dealloc]
    
    • 中间件解决NSTimer的target循环引用原理 中间件解决NSTimer的target循环引用.png

    实践二

    • 使用API
    /**
       定时器
    
       @param interval 时间间隔
       @param repeats 是否重复
       @param block 任务block
    */
    + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
    
    • 测试代码
    #import "ViewController.h"
    
    @interface ViewController ()
    
    /** NSTimer */
    @property (nonatomic, strong) NSTimer *timer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
            [self timerFun];
        }];
    }
    
    - (void)timerFun {
        
        NSLog(@"%s --%@", __func__, [NSThread currentThread]);
        
    }
    
    - (void)dealloc {
        NSLog(@"%s", __func__);
        [self.timer invalidate];
        self.timer = nil;
    }
    
    @end
    
    • 打印输出
    2018-07-25 14:41:52.997761+0800 NSTimer[12605:235494] -[ViewController timerFun] --<NSThread: 0x604000079c40>{number = 1, name = main}
    2018-07-25 14:41:53.998005+0800 NSTimer[12605:235494] -[ViewController timerFun] --<NSThread: 0x604000079c40>{number = 1, name = main}
    2018-07-25 14:41:54.997632+0800 NSTimer[12605:235494] -[ViewController timerFun] --<NSThread: 0x604000079c40>{number = 1, name = main}
    2018-07-25 14:41:55.997932+0800 NSTimer[12605:235494] -[ViewController timerFun] --<NSThread: 0x604000079c40>{number = 1, name = main}
    2018-07-25 14:41:56.997607+0800 NSTimer[12605:235494] -[ViewController timerFun] --<NSThread: 0x604000079c40>{number = 1, name = main}
    2018-07-25 14:41:57.998040+0800 NSTimer[12605:235494] -[ViewController timerFun] --<NSThread: 0x604000079c40>{number = 1, name = main}
    2018-07-25 14:41:58.997908+0800 NSTimer[12605:235494] -[ViewController timerFun] --<NSThread: 0x604000079c40>{number = 1, name = main}
    2018-07-25 14:41:59.997616+0800 NSTimer[12605:235494] -[ViewController timerFun] --<NSThread: 0x604000079c40>{number = 1, name = main}
    2018-07-25 14:42:00.998211+0800 NSTimer[12605:235494] -[ViewController timerFun] --<NSThread: 0x604000079c40>{number = 1, name = main}
    
    • 问题:依旧存在循环引用 NSTimer_block循环引用.png

    1、使用__weak来解决NSTimer的block循环引用

    • 测试代码
    #import "ViewController.h"
    
    @interface ViewController ()
    
    /** NSTimer */
    @property (nonatomic, strong) NSTimer *timer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        __weak typeof(self) weakSelf = self;
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
            [weakSelf timerFun];
        }];
        
    }
    
    - (void)timerFun {
        
        NSLog(@"%s --%@", __func__, [NSThread currentThread]);
        
    }
    
    - (void)dealloc {
        NSLog(@"%s", __func__);
        [self.timer invalidate];
        self.timer = nil;
    }
    
    @end
    
    • 打印输出
    2018-07-25 14:52:21.773549+0800 NSTimer[13007:244135] -[ViewController timerFun] --<NSThread: 0x60000007d2c0>{number = 1, name = main}
    2018-07-25 14:52:22.773903+0800 NSTimer[13007:244135] -[ViewController timerFun] --<NSThread: 0x60000007d2c0>{number = 1, name = main}
    2018-07-25 14:52:23.773437+0800 NSTimer[13007:244135] -[ViewController timerFun] --<NSThread: 0x60000007d2c0>{number = 1, name = main}
    2018-07-25 14:52:24.359867+0800 NSTimer[13007:244135] -[ViewController dealloc]
    
    • __weak解决NSTimer的block循环引用原理 __weak解决NSTimer_block的循环引用.png
    • 小结
      使用以scheduled开头的定时器API时,定时器已经加入到了当前runloop中

    实践三

    • 使用API
    /**
       创建定时器
    
       @param ti 时间间隔
       @param aTarget 执行任务target
       @param aSelector 需要执行的任务
       @param userInfo 参数
       @param yesOrNo 是否重复
    */
    + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
    
    • 测试代码
    
    #import "ViewController.h"
    
    @interface ViewController ()
    
    /** NSTimer */
    @property (nonatomic, strong) NSTimer *timer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerFun) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
        //子线程runloop需要手动开启
    //    [NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture];
    }
    
    - (void)timerFun {
        
        NSLog(@"%s --%@", __func__, [NSThread currentThread]);
        
    }
    
    - (void)dealloc {
        NSLog(@"%s", __func__);
        [self.timer invalidate];
        self.timer = nil;
    }
    
    @end
    
    • 打印输出
    2018-07-25 17:40:31.560835+0800 NSTimer[17231:373102] -[ViewController timerFun] --<NSThread: 0x604000260c80>{number = 1, name = main}
    2018-07-25 17:40:32.560742+0800 NSTimer[17231:373102] -[ViewController timerFun] --<NSThread: 0x604000260c80>{number = 1, name = main}
    2018-07-25 17:40:33.560538+0800 NSTimer[17231:373102] -[ViewController timerFun] --<NSThread: 0x604000260c80>{number = 1, name = main}
    2018-07-25 17:40:34.560385+0800 NSTimer[17231:373102] -[ViewController timerFun] --<NSThread: 0x604000260c80>{number = 1, name = main}
    2018-07-25 17:40:35.560674+0800 NSTimer[17231:373102] -[ViewController timerFun] --<NSThread: 0x604000260c80>{number = 1, name = main}
    
    • 问题:存在循环引用 NSTimer的target循环引用.png

    解决循环引用

    因为是NSTimer的target引用所以我在选择中间件来解决循环应用

    • 测试代码
    • 1、中间件RevanProxy
    #import <Foundation/Foundation.h>
    
    @interface RevanProxy : NSObject
    
    /**
     构造中间件
     */
    +(instancetype)revan_proxy:(id)target;
    
    @end
    
    #import "RevanProxy.h"
    
    @interface RevanProxy ()
    
    /** target */
    @property (nonatomic, weak) id target;
    
    @end
    
    @implementation RevanProxy
    
    /**
     构造中间件
     */
    +(instancetype)revan_proxy:(id)target {
        RevanProxy *proxy = [[RevanProxy alloc] init];
        proxy.target = target;
        return proxy;
    }
    
    
    /**
     消息转发
    
     @param aSelector 消息
     @return 执行消息的对象
     */
    - (id)forwardingTargetForSelector:(SEL)aSelector {
        return self.target;
    }
    
    @end
    
    • 测试代码
    #import "ViewController.h"
    #import "RevanProxy.h"
    
    @interface ViewController ()
    
    /** NSTimer */
    @property (nonatomic, strong) NSTimer *timer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.timer = [NSTimer timerWithTimeInterval:1.0 target:[RevanProxy revan_proxy:self] selector:@selector(timerFun) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
        //子线程runloop需要手动开启
    //    [NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture];
    }
    
    - (void)timerFun {
        
        NSLog(@"%s --%@", __func__, [NSThread currentThread]);
        
    }
    
    - (void)dealloc {
        NSLog(@"%s", __func__);
        [self.timer invalidate];
        self.timer = nil;
    }
    
    @end
    
    • 打印输出
    2018-07-25 17:43:15.562021+0800 NSTimer[17310:375348] -[ViewController timerFun] --<NSThread: 0x60000006e500>{number = 1, name = main}
    2018-07-25 17:43:16.562049+0800 NSTimer[17310:375348] -[ViewController timerFun] --<NSThread: 0x60000006e500>{number = 1, name = main}
    2018-07-25 17:43:17.089576+0800 NSTimer[17310:375348] -[ViewController dealloc]
    
    • 解决循环引用原理 NSTimer的target循环引用-中间件方式.png

    实践四

    • 使用API
    /**
       定时器
    
       @param interval 间隔时间
       @param repeats 重复
       @param block 任务block
    */
    + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
    
    • 测试代码
    #import "ViewController.h"
    
    @interface ViewController ()
    
    /** NSTimer */
    @property (nonatomic, strong) NSTimer *timer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
            [self timerFun];
        }];
        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
        //子线程runloop需要手动开启
    //    [NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture];
    }
    
    - (void)timerFun {
        
        NSLog(@"%s --%@", __func__, [NSThread currentThread]);
        
    }
    
    - (void)dealloc {
        NSLog(@"%s", __func__);
        [self.timer invalidate];
        self.timer = nil;
    }
    
    @end
    
    • 打印输出
    2018-07-25 17:52:52.583498+0800 NSTimer[17610:384088] -[ViewController timerFun] --<NSThread: 0x604000066b00>{number = 1, name = main}
    2018-07-25 17:52:53.582260+0800 NSTimer[17610:384088] -[ViewController timerFun] --<NSThread: 0x604000066b00>{number = 1, name = main}
    2018-07-25 17:52:54.582224+0800 NSTimer[17610:384088] -[ViewController timerFun] --<NSThread: 0x604000066b00>{number = 1, name = main}
    2018-07-25 17:52:55.582232+0800 NSTimer[17610:384088] -[ViewController timerFun] --<NSThread: 0x604000066b00>{number = 1, name = main}
    2018-07-25 17:52:56.582231+0800 NSTimer[17610:384088] -[ViewController timerFun] --<NSThread: 0x604000066b00>{number = 1, name = main}
    2018-07-25 17:52:57.583263+0800 NSTimer[17610:384088] -[ViewController timerFun] --<NSThread: 0x604000066b00>{number = 1, name = main}
    
    • 问题
      • 存在循环引用 NSTimer的block的循环引用.png

    解决NSTimer的block循环

    • 测试代码
    #import "ViewController.h"
    
    @interface ViewController ()
    
    /** NSTimer */
    @property (nonatomic, strong) NSTimer *timer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        __weak typeof(self) weakSelf = self;
        self.timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
            [weakSelf timerFun];
        }];
        
        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
        //子线程runloop需要手动开启
    //    [NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture];
    }
    
    - (void)timerFun {
        
        NSLog(@"%s --%@", __func__, [NSThread currentThread]);
        
    }
    
    - (void)dealloc {
        NSLog(@"%s", __func__);
        [self.timer invalidate];
        self.timer = nil;
    }
    
    @end
    
    • 打印输出
    2018-07-25 18:00:47.782248+0800 NSTimer[17767:390307] -[ViewController timerFun] --<NSThread: 0x60400007f6c0>{number = 1, name = main}
    2018-07-25 18:00:48.782537+0800 NSTimer[17767:390307] -[ViewController timerFun] --<NSThread: 0x60400007f6c0>{number = 1, name = main}
    2018-07-25 18:00:49.782184+0800 NSTimer[17767:390307] -[ViewController timerFun] --<NSThread: 0x60400007f6c0>{number = 1, name = main}
    2018-07-25 18:00:50.782352+0800 NSTimer[17767:390307] -[ViewController timerFun] --<NSThread: 0x60400007f6c0>{number = 1, name = main}
    2018-07-25 18:00:51.056397+0800 NSTimer[17767:390307] -[ViewController dealloc]
    
    • 解决循环引用原理 NSTimer的block循环引用的解决.png

    小结

    • 使用timer开头的API时,需要把定时器手动加入当前runloop,如果是子线程还需要开启当前子线程
    • 使用scheduled开头的API时,当在主线程时不需要把定时器手动加入当前runloop;当在子线程时同样需要吧定时器手动加入子线程runloop中同时开启子线程

    二、CADisplayLink

    • 测试代码
    #import "ViewController.h"
    
    @interface ViewController ()
    
    /** CADisplayLink */
    @property (nonatomic, strong) CADisplayLink *displayLink;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timerFun)];
        
        [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
    
    - (void)timerFun {
        
        NSLog(@"%s --%@", __func__, [NSThread currentThread]);
        
    }
    
    - (void)dealloc {
        NSLog(@"%s", __func__);
        [self.displayLink invalidate];
        self.displayLink = nil;
    }
    
    @end
    
    • 打印输出
    2018-07-25 18:29:21.257630+0800 NSTimer[18426:414584] -[ViewController timerFun] --<NSThread: 0x60000006fbc0>{number = 1, name = main}
    2018-07-25 18:29:21.260929+0800 NSTimer[18426:414584] -[ViewController timerFun] --<NSThread: 0x60000006fbc0>{number = 1, name = main}
    2018-07-25 18:29:21.277595+0800 NSTimer[18426:414584] -[ViewController timerFun] --<NSThread: 0x60000006fbc0>{number = 1, name = main}
    2018-07-25 18:29:21.294965+0800 NSTimer[18426:414584] -[ViewController timerFun] --<NSThread: 0x60000006fbc0>{number = 1, name = main}
    2018-07-25 18:29:21.310950+0800 NSTimer[18426:414584] -[ViewController timerFun] --<NSThread: 0x60000006fbc0>{number = 1, name = main}
    2018-07-25 18:29:21.328056+0800 NSTimer[18426:414584] -[ViewController timerFun] --<NSThread: 0x60000006fbc0>{number = 1, name = main}
    2018-07-25 18:29:21.344301+0800 NSTimer[18426:414584] -[ViewController timerFun] --<NSThread: 0x60000006fbc0>{number = 1, name = main}
    
    • 问题:存在循环引用 CADisplayLink循环引用.png

    使用中间件RevanProxy解决CADisplayLink循环引用

    • RevanProxy
    #import <Foundation/Foundation.h>
    
    @interface RevanProxy : NSObject
    
    /**
     构造中间件
     */
    +(instancetype)revan_proxy:(id)target;
    
    @end
    
    
    #import "RevanProxy.h"
    
    @interface RevanProxy ()
    
    /** target */
    @property (nonatomic, weak) id target;
    
    @end
    
    @implementation RevanProxy
    
    /**
     构造中间件
     */
    +(instancetype)revan_proxy:(id)target {
        RevanProxy *proxy = [[RevanProxy alloc] init];
        proxy.target = target;
        return proxy;
    }
    
    
    /**
     消息转发
    
     @param aSelector 消息
     @return 执行消息的对象
     */
    - (id)forwardingTargetForSelector:(SEL)aSelector {
        return self.target;
    }
    
    @end
    
    • 测试代码
    
    #import "ViewController.h"
    #import "RevanProxy.h"
    
    @interface ViewController ()
    
    /** CADisplayLink */
    @property (nonatomic, strong) CADisplayLink *displayLink;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.displayLink = [CADisplayLink displayLinkWithTarget:[RevanProxy revan_proxy:self] selector:@selector(timerFun)];
        
        [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
    
    - (void)timerFun {
        
        NSLog(@"%s --%@", __func__, [NSThread currentThread]);
        
    }
    
    - (void)dealloc {
        NSLog(@"%s", __func__);
        [self.displayLink invalidate];
        self.displayLink = nil;
    }
    
    @end
    
    • 打印输出
    2018-07-25 18:37:04.564087+0800 NSTimer[18612:422374] -[ViewController timerFun] --<NSThread: 0x604000064880>{number = 1, name = main}
    2018-07-25 18:37:04.580233+0800 NSTimer[18612:422374] -[ViewController timerFun] --<NSThread: 0x604000064880>{number = 1, name = main}
    2018-07-25 18:37:04.597601+0800 NSTimer[18612:422374] -[ViewController timerFun] --<NSThread: 0x604000064880>{number = 1, name = main}
    2018-07-25 18:37:04.613840+0800 NSTimer[18612:422374] -[ViewController timerFun] --<NSThread: 0x604000064880>{number = 1, name = main}
    2018-07-25 18:37:04.630309+0800 NSTimer[18612:422374] -[ViewController timerFun] --<NSThread: 0x604000064880>{number = 1, name = main}
    2018-07-25 18:37:04.647850+0800 NSTimer[18612:422374] -[ViewController timerFun] --<NSThread: 0x604000064880>{number = 1, name = main}
    2018-07-25 18:37:04.664557+0800 NSTimer[18612:422374] -[ViewController timerFun] --<NSThread: 0x604000064880>{number = 1, name = main}
    2018-07-25 18:37:04.680940+0800 NSTimer[18612:422374] -[ViewController timerFun] --<NSThread: 0x604000064880>{number = 1, name = main}
    2018-07-25 18:37:04.688731+0800 NSTimer[18612:422374] -[ViewController dealloc]
    
    • 解决循环引用原理 CADisplayLink解决循环引用.png

    三、GCD定时器

    • 测试代码
    #import "ViewController.h"
    
    @interface ViewController ()
    @property (nonatomic, strong) dispatch_source_t gcdTimer;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //1.队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        //2.创建定时器
        self.gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        //3.设置时间
        // start 秒后开始执行
        uint64_t start = 2.0;
        // 每隔interval执行
        uint64_t interval = 1.0;
        
        dispatch_source_set_timer(self.gcdTimer, dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC), interval * NSEC_PER_SEC, 0);
        
        //4.设置回调
        dispatch_source_set_event_handler(self.gcdTimer, ^{
            [self timerFire];
        });
        
        //5.启动定时器
        dispatch_resume(self.gcdTimer);
    }
    
    - (void)timerFire {
        NSLog(@"2222 - %@", [NSThread currentThread]);
    }
    
    - (void)dealloc {
        self.gcdTimer = nil;
        NSLog(@"%s", __func__);
    }
    
    @end
    
    • 打印输出
    2018-07-25 23:26:07.602187+0800 09-GCD定时器[37249:2261911] 2222 - <NSThread: 0x600000061b80>{number = 1, name = main}
    2018-07-25 23:26:08.602584+0800 09-GCD定时器[37249:2261911] 2222 - <NSThread: 0x600000061b80>{number = 1, name = main}
    2018-07-25 23:26:09.602216+0800 09-GCD定时器[37249:2261911] 2222 - <NSThread: 0x600000061b80>{number = 1, name = main}
    2018-07-25 23:26:10.602594+0800 09-GCD定时器[37249:2261911] 2222 - <NSThread: 0x600000061b80>{number = 1, name = main}
    2018-07-25 23:26:11.602276+0800 09-GCD定时器[37249:2261911] 2222 - <NSThread: 0x600000061b80>{number = 1, name = main}
    2018-07-25 23:26:12.601514+0800 09-GCD定时器[37249:2261911] 2222 - <NSThread: 0x600000061b80>{number = 1, name = main}
    
    • 问题:依然存在block的循环引用,和上面的NSTimer的block循环原理是一样的

    解决GCD中block循环引用

    #import "ViewController.h"
    
    @interface ViewController ()
    @property (nonatomic, strong) dispatch_source_t gcdTimer;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //1.队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        //2.创建定时器
        self.gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        //3.设置时间
        // start 秒后开始执行
        uint64_t start = 2.0;
        // 每隔interval执行
        uint64_t interval = 1.0;
        
        dispatch_source_set_timer(self.gcdTimer, dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC), interval * NSEC_PER_SEC, 0);
        
        //4.设置回调
        __weak typeof(self)weakSelf = self;
        dispatch_source_set_event_handler(self.gcdTimer, ^{
            [weakSelf timerFire];
        });
        //5.启动定时器
        dispatch_resume(self.gcdTimer);
    }
    
    - (void)timerFire {
        NSLog(@"%s - %@", __func__, [NSThread currentThread]);
    }
    
    - (void)dealloc {
        self.gcdTimer = nil;
        NSLog(@"%s", __func__);
    }
    
    @end
    
    
    • 打印输出
    2018-07-25 23:30:56.361956+0800 09-GCD定时器[37317:2266010] -[ViewController timerFire] - <NSThread: 0x60400006be80>{number = 1, name = main}
    2018-07-25 23:30:57.361294+0800 09-GCD定时器[37317:2266010] -[ViewController timerFire] - <NSThread: 0x60400006be80>{number = 1, name = main}
    2018-07-25 23:30:58.362232+0800 09-GCD定时器[37317:2266010] -[ViewController timerFire] - <NSThread: 0x60400006be80>{number = 1, name = main}
    2018-07-25 23:30:59.361211+0800 09-GCD定时器[37317:2266010] -[ViewController timerFire] - <NSThread: 0x60400006be80>{number = 1, name = main}
    2018-07-25 23:30:59.495951+0800 09-GCD定时器[37317:2266010] -[ViewController dealloc]
    

    五、小结

    • 如果是使用定时器,推荐使用GCD,定时相比来说比较准确
    • 上面验证的都是在主线程下NSTimer、CADisplayLink、GCD的循环引用
    • 定时器在子线程中的循环引用

    相关文章

      网友评论

        本文标题:NSTimer、CADisplayLink、GCD定时器

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