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];
}];
通过消息转发解决循环引用的问题

#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");
}
网友评论