NSTimer、CADisplayLink、GCD定时器

一、NSTimer

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

测试NSTimer的工程介绍

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

NSTimer、CADisplayLink、GCD定时器_第1张图片
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:] --{number = 1, name = main}--参数
2018-07-25 11:00:54.488425+0800 NSTimer[2046:70846] -[ViewController timerFun:] --{number = 1, name = main}--参数
2018-07-25 11:00:55.487701+0800 NSTimer[2046:70846] -[ViewController timerFun:] --{number = 1, name = main}--参数
2018-07-25 11:00:56.487452+0800 NSTimer[2046:70846] -[ViewController timerFun:] --{number = 1, name = main}--参数
  • 问题
    • 当点击Back时,定时器并没有停止工作,而且ViewController也没有销毁,NSTimer造成了循环引用
      NSTimer、CADisplayLink、GCD定时器_第2张图片
      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:] --{number = 1, name = main}--参数
2018-07-25 11:21:01.788937+0800 NSTimer[2325:86420] -[ViewController timerFun:] --{number = 1, name = main}--参数
2018-07-25 11:21:02.788234+0800 NSTimer[2325:86420] -[ViewController timerFun:] --{number = 1, name = main}--参数
2018-07-25 11:21:03.787947+0800 NSTimer[2325:86420] -[ViewController timerFun:] --{number = 1, name = main}--参数
2018-07-25 11:21:04.788652+0800 NSTimer[2325:86420] -[ViewController timerFun:] --{number = 1, name = main}--参数
2018-07-25 11:21:05.787843+0800 NSTimer[2325:86420] -[ViewController timerFun:] --{number = 1, name = main}--参数
  • 使用__weak是没有办法解决NSTimer的target循环引用
  • NSTimer内部有一个属性来保存外界传入的target的,所以无论外界传入的target是weakSelf还是self,对于NSTimer内部来说都会强引用你传入的target

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

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

  • 测试代码
  • 1、中间件RevanProxy
#import 

@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:] --{number = 1, name = main}--参数
2018-07-25 11:39:03.231457+0800 NSTimer[2581:99832] -[ViewController timerFun:] --{number = 1, name = main}--参数
2018-07-25 11:39:04.230871+0800 NSTimer[2581:99832] -[ViewController timerFun:] --{number = 1, name = main}--参数
2018-07-25 11:39:04.558871+0800 NSTimer[2581:99832] -[ViewController dealloc]
  • 中间件解决NSTimer的target循环引用原理
    NSTimer、CADisplayLink、GCD定时器_第3张图片
    中间件解决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] --{number = 1, name = main}
2018-07-25 14:41:53.998005+0800 NSTimer[12605:235494] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 14:41:54.997632+0800 NSTimer[12605:235494] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 14:41:55.997932+0800 NSTimer[12605:235494] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 14:41:56.997607+0800 NSTimer[12605:235494] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 14:41:57.998040+0800 NSTimer[12605:235494] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 14:41:58.997908+0800 NSTimer[12605:235494] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 14:41:59.997616+0800 NSTimer[12605:235494] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 14:42:00.998211+0800 NSTimer[12605:235494] -[ViewController timerFun] --{number = 1, name = main}
  • 问题:依旧存在循环引用
    NSTimer、CADisplayLink、GCD定时器_第4张图片
    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] --{number = 1, name = main}
2018-07-25 14:52:22.773903+0800 NSTimer[13007:244135] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 14:52:23.773437+0800 NSTimer[13007:244135] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 14:52:24.359867+0800 NSTimer[13007:244135] -[ViewController dealloc]
  • __weak解决NSTimer的block循环引用原理
    NSTimer、CADisplayLink、GCD定时器_第5张图片
    __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] --{number = 1, name = main}
2018-07-25 17:40:32.560742+0800 NSTimer[17231:373102] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 17:40:33.560538+0800 NSTimer[17231:373102] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 17:40:34.560385+0800 NSTimer[17231:373102] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 17:40:35.560674+0800 NSTimer[17231:373102] -[ViewController timerFun] --{number = 1, name = main}
  • 问题:存在循环引用
    NSTimer、CADisplayLink、GCD定时器_第6张图片
    NSTimer的target循环引用.png

解决循环引用

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

  • 测试代码
  • 1、中间件RevanProxy
#import 

@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] --{number = 1, name = main}
2018-07-25 17:43:16.562049+0800 NSTimer[17310:375348] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 17:43:17.089576+0800 NSTimer[17310:375348] -[ViewController dealloc]
  • 解决循环引用原理
    NSTimer、CADisplayLink、GCD定时器_第7张图片
    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] --{number = 1, name = main}
2018-07-25 17:52:53.582260+0800 NSTimer[17610:384088] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 17:52:54.582224+0800 NSTimer[17610:384088] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 17:52:55.582232+0800 NSTimer[17610:384088] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 17:52:56.582231+0800 NSTimer[17610:384088] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 17:52:57.583263+0800 NSTimer[17610:384088] -[ViewController timerFun] --{number = 1, name = main}
  • 问题
    • 存在循环引用
      NSTimer、CADisplayLink、GCD定时器_第8张图片
      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] --{number = 1, name = main}
2018-07-25 18:00:48.782537+0800 NSTimer[17767:390307] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:00:49.782184+0800 NSTimer[17767:390307] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:00:50.782352+0800 NSTimer[17767:390307] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:00:51.056397+0800 NSTimer[17767:390307] -[ViewController dealloc]
  • 解决循环引用原理
    NSTimer、CADisplayLink、GCD定时器_第9张图片
    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] --{number = 1, name = main}
2018-07-25 18:29:21.260929+0800 NSTimer[18426:414584] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:29:21.277595+0800 NSTimer[18426:414584] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:29:21.294965+0800 NSTimer[18426:414584] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:29:21.310950+0800 NSTimer[18426:414584] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:29:21.328056+0800 NSTimer[18426:414584] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:29:21.344301+0800 NSTimer[18426:414584] -[ViewController timerFun] --{number = 1, name = main}
  • 问题:存在循环引用
    NSTimer、CADisplayLink、GCD定时器_第10张图片
    CADisplayLink循环引用.png

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

  • RevanProxy
#import 

@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] --{number = 1, name = main}
2018-07-25 18:37:04.580233+0800 NSTimer[18612:422374] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:37:04.597601+0800 NSTimer[18612:422374] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:37:04.613840+0800 NSTimer[18612:422374] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:37:04.630309+0800 NSTimer[18612:422374] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:37:04.647850+0800 NSTimer[18612:422374] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:37:04.664557+0800 NSTimer[18612:422374] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:37:04.680940+0800 NSTimer[18612:422374] -[ViewController timerFun] --{number = 1, name = main}
2018-07-25 18:37:04.688731+0800 NSTimer[18612:422374] -[ViewController dealloc]
  • 解决循环引用原理
    NSTimer、CADisplayLink、GCD定时器_第11张图片
    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 - {number = 1, name = main}
2018-07-25 23:26:08.602584+0800 09-GCD定时器[37249:2261911] 2222 - {number = 1, name = main}
2018-07-25 23:26:09.602216+0800 09-GCD定时器[37249:2261911] 2222 - {number = 1, name = main}
2018-07-25 23:26:10.602594+0800 09-GCD定时器[37249:2261911] 2222 - {number = 1, name = main}
2018-07-25 23:26:11.602276+0800 09-GCD定时器[37249:2261911] 2222 - {number = 1, name = main}
2018-07-25 23:26:12.601514+0800 09-GCD定时器[37249:2261911] 2222 - {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] - {number = 1, name = main}
2018-07-25 23:30:57.361294+0800 09-GCD定时器[37317:2266010] -[ViewController timerFire] - {number = 1, name = main}
2018-07-25 23:30:58.362232+0800 09-GCD定时器[37317:2266010] -[ViewController timerFire] - {number = 1, name = main}
2018-07-25 23:30:59.361211+0800 09-GCD定时器[37317:2266010] -[ViewController timerFire] - {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定时器)