目录
定时器的循环引用
一、如何产生了循环引用
@interface ViewController ()
@property (nonatomic, strong) NSTimer * timer;
@property (strong, nonatomic) CADisplayLink *link;
@end
@implementation ViewController
- (void)viewDidLoad{
[super viewDidLoad];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(test)
userInfo:nil
repeats:YES];
// 保证调用频率和屏幕的刷帧频率一致,60FPS
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(test)];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
-(void)test{
NSLog(@"定时器调用");
}
@end
上面的两个定时器存在强引用问题,self强引用timer,timer相关接口要求传入self,并且在合适的时间向self发送消息@selector(test),对self也是强引用。这样就会造成循环引用问题,导致内存泄漏。
二、如何解决
1、方式一:使用block
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
repeats:YES
block:^(NSTimer * _Nonnull timer) {
NSLog(@"定时器调用");
}];
2、使用代理破坏循环引用结构
创建一个代理类MJProxy
MJProxy.h文件
#import <Foundation/Foundation.h>
@interface MJProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end
MJProxy.m文件
#import "MJProxy.h"
@implementation MJProxy
+ (instancetype)proxyWithTarget:(id)target
{
// NSProxy对象不需要调用init,因为它本来就没有init方法
MJProxy *proxy = [MJProxy alloc];
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation invokeWithTarget:self.target];
}
@end
调用
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:[MJProxy proxyWithTarget:self]
selector:@selector(timerTest)
userInfo:nil
repeats:YES];
调整之后指针的指向及强弱引用如下:
定时器的偏差问题
NSTimer依赖于RunLoop,如果RunLoop任务过于繁重,可能导致NSTimer不准时
而GCD的定时器更加准时
- (void)test{
// 队列
// dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_queue_t queue = dispatch_queue_create("timer", DISPATCH_QUEUE_SERIAL);
// 创建定时器
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(@"1111");
// });
dispatch_source_set_event_handler_f(timer, timerFire);
// 启动定时器
dispatch_resume(timer);
self.timer = timer;
}
void timerFire(void *param){
NSLog(@"2222 - %@", [NSThread currentThread]);
}
行者常至,为者常成!