CADisplayLink NSTimer NSProxy

2018-09-14  本文已影响3人  曹来东

CADisplayLink NSTimer

下面的代码会造成循环引用,控制器和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];
    }];

通过消息转发解决循环引用的问题

image.png
#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定时器

//队列
    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定时器封装


#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");

}

上一篇下一篇

猜你喜欢

热点阅读