NSTimer、CADisplayLink、GCD定时器
2018-07-25 本文已影响30人
紫荆秋雪_文
一、NSTimer
- NSTimer和CADisplayLink依赖于RunLoop,如果RunLoop的任务过于繁重,可能会导致NSTimer不准时,相比之下GCD的定时器会更加准时,因为GCD不是依赖RunLoop,而是由内核决定
- CADisplayLink和NSTimer会对target产生强引用,如果target又对它们产生强引用,那么就会引发循环引用
测试NSTimer的工程介绍
NSTimer工程介绍.png点击NSTimerBtn跳转到最左边控制器,NSTimer开始工作;点击Back时定时器停止工作
实例一
- 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的循环引用
- 定时器在子线程中的循环引用