iOS:三种定时器

目录
一,NSTimer
二,CADisplayLink
三,dispatch_source_timer

一,NSTimer

1,基本使用

  • timerWithTimeInterval:
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
                                            repeats:YES
                                              block:^(NSTimer * _Nonnull timer) {
        NSLog(@"timer running");
    }];
    [[NSRunLoop currentRunLoop] addTimer:timer
                                 forMode:NSDefaultRunLoopMode];
}

// 打印
07:21:30.266493+0800 Demo[10449:9624199] timer running
07:21:31.266367+0800 Demo[10449:9624199] timer running
07:21:32.266795+0800 Demo[10449:9624199] timer running
  • scheduledTimerWithTimeInterval:
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 自动添加到runloop的NSDefaultRunLoopMode中
    [NSTimer scheduledTimerWithTimeInterval:1.0
                                    repeats:YES
                                      block:^(NSTimer * _Nonnull timer) {
        NSLog(@"timer running");
    }];
}

// 打印
07:24:10.249589+0800 Demo[10584:9626453] timer running
07:24:11.250144+0800 Demo[10584:9626453] timer running
07:24:12.250318+0800 Demo[10584:9626453] timer running
  • initWithFireDate:
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"begin");
    NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:3.0]
                                              interval:1.0
                                               repeats:YES
                                                 block:^(NSTimer * _Nonnull timer) {
        NSLog(@"timer running");
    }];
    [[NSRunLoop currentRunLoop] addTimer:timer
                                 forMode:NSDefaultRunLoopMode];
}

// 打印
07:32:16.943407+0800 Demo[10738:9632602] begin
07:32:19.944573+0800 Demo[10738:9632602] timer running
07:32:20.944960+0800 Demo[10738:9632602] timer running
07:32:21.944652+0800 Demo[10738:9632602] timer running

2,解决循环引用

/**
 self强引用timer,timer强引用proxy,proxy弱引用self
 */

// YJProxy.h
@interface YJProxy : NSObject
+ (instancetype)proxyWithTarget:(id)target;
@end

// YJProxy.m
@interface YJProxy ()
@property (nonatomic, weak) id target; // 弱引用
@end

@implementation YJProxy
+ (instancetype)proxyWithTarget:(id)target {
    YJProxy *proxy = [[YJProxy alloc] init];
    proxy.target = target;
    return proxy;
}
- (id)forwardingTargetForSelector:(SEL)aSelector { // 消息转发
    return self.target;
}
@end

// ViewController
@interface ViewController ()
@property (nonatomic, strong) NSTimer *timer;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                                  target:[YJProxy proxyWithTarget:self]
                                                selector:@selector(timerTask)
                                                userInfo:nil
                                                 repeats:YES];
}
- (void)timerTask {
    NSLog(@"%s", __func__);
}
- (void)dealloc {
    NSLog(@"%s", __func__);
    [self.timer invalidate];
}
@end

// 打印
07:56:14.897620+0800 Demo[11187:9646035] -[ViewController timerTask]
07:56:15.897446+0800 Demo[11187:9646035] -[ViewController timerTask]
07:56:16.898022+0800 Demo[11187:9646035] -[ViewController timerTask]
07:56:17.860300+0800 Demo[11187:9646035] -[ViewController dealloc]

3,NSProxy

  • 使用
@interface YJProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@end

@interface YJProxy ()
@property (nonatomic, weak) id target;
@end

@implementation YJProxy
+ (instancetype)proxyWithTarget:(id)target {
    // NSProxy没有init方法
    YJProxy *proxy = [YJProxy alloc];
    proxy.target = target;
    return proxy;
}
// 消息转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
    [invocation invokeWithTarget:self.target];
}
@end
  • 说明

1>它跟NSObject是同一级别的

2>它专用于消息转发

3>用它做代理对象效率更高,因为它是直接执行消息转发的,而NSObject需要先执行消息发送和动态方法解析

二,CADisplayLink

1,使用

@interface ViewController ()
@property (nonatomic, strong) CADisplayLink *link;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.link = [CADisplayLink displayLinkWithTarget:[YJProxy proxyWithTarget:self]
                                            selector:@selector(linkTask)];
    // 设置每秒执行1次
    self.link.preferredFramesPerSecond = 1;
    [self.link addToRunLoop:[NSRunLoop currentRunLoop]
                    forMode:NSDefaultRunLoopMode];
}
- (void)linkTask {
    NSLog(@"%s", __func__);
    // 下一次执行的时间 - 当前时间
    NSLog(@"%f", self.link.targetTimestamp - self.link.timestamp);
    // 两帧之间的时间间隔
    NSLog(@"%f", self.link.duration);
}
- (void)dealloc {
    NSLog(@"%s", __func__);
    [self.link invalidate];
}
@end

// 打印
13:26:37.176374+0800 Demo[15813:9765865] -[ViewController linkTask]
13:26:37.176581+0800 Demo[15813:9765865] 1.000000
13:26:37.176682+0800 Demo[15813:9765865] 0.016667
13:26:38.172891+0800 Demo[15813:9765865] -[ViewController linkTask]
13:26:38.173096+0800 Demo[15813:9765865] 1.000000
13:26:38.173237+0800 Demo[15813:9765865] 0.016667
13:26:39.173083+0800 Demo[15813:9765865] -[ViewController linkTask]
13:26:39.173282+0800 Demo[15813:9765865] 1.000000
13:26:39.173442+0800 Demo[15813:9765865] 0.016667
13:26:39.207185+0800 Demo[15813:9765865] -[ViewController dealloc]

2,说明

  • 它是一个与屏幕刷新相关的定时器

  • 屏幕刷新频率是60FPS,所以它默认每秒执行60次

  • duration是按照60FPS来计算的(1 / 60 = 0.016667)

三,dispatch_source_timer

1,使用

@interface ViewController ()
@property (nonatomic, strong) dispatch_source_t timer;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 创建定时器并设置执行任务的队列
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    // 每1秒执行一次任务并允许有1秒的误差
    dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 1.0 * NSEC_PER_SEC);
    
    // 定时器任务
    dispatch_source_set_event_handler(self.timer, ^{
        NSLog(@"timer running---%@", [NSThread currentThread]);
    });
    
    // 定时器被取消的回调
    dispatch_source_set_cancel_handler(self.timer, ^{
        NSLog(@"timer cancelled---%@", [NSThread currentThread]);
    });
    
    // 启动定时器
    dispatch_resume(self.timer);
}
- (void)dealloc {
    NSLog(@"%s", __func__);
    // 取消定时器
    dispatch_source_cancel(self.timer);
}
@end

// 打印
15:15:24.073246+0800 Demo[17534:9809826] timer running---<NSThread: 0x6000035f2bc0>{number = 8, name = (null)}
15:15:25.073340+0800 Demo[17534:9809827] timer running---<NSThread: 0x6000035c5000>{number = 8, name = (null)}
15:15:26.155698+0800 Demo[17534:9809827] timer running---<NSThread: 0x6000035c5000>{number = 6, name = (null)}
15:15:27.247158+0800 Demo[17534:9809775] -[ViewController dealloc]
15:15:27.247717+0800 Demo[17534:9809827] timer cancelled---<NSThread: 0x6000035c5000>{number = 5, name = (null)}

2,封装

// YJTimer.h
@interface YJTimer : NSObject
- (instancetype)initWithInterval:(NSTimeInterval)interval
                         repeats:(BOOL)repeats
                           async:(BOOL)async
                           block:(void(^)(YJTimer *timer))block;
- (void)start;
- (void)cancel;
@end

// YJTimer.m
@interface YJTimer ()
@property (nonatomic, strong) dispatch_source_t timer;
@end

@implementation YJTimer
- (instancetype)initWithInterval:(NSTimeInterval)interval
                         repeats:(BOOL)repeats
                           async:(BOOL)async
                           block:(void(^)(YJTimer *timer))block {
    if (self = [super init]) {
        dispatch_queue_t queue;
        if (async) {
            queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        } else {
            queue = dispatch_get_main_queue();
        }

        self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, interval * NSEC_PER_SEC, 0.0 * NSEC_PER_SEC);
        
        __weak typeof(self) weakSelf = self;
        dispatch_source_set_event_handler(self.timer, ^{
            if (block) {
                block(weakSelf);
            }
            if (!repeats) {
                [weakSelf cancel];
            }
        });
    }
    return self;
}
- (void)start {
    dispatch_resume(self.timer);
}
- (void)cancel {
    dispatch_source_cancel(self.timer);
}
@end

// ViewController
@interface ViewController ()
@property (nonatomic, strong) YJTimer *timer;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.timer = [[YJTimer alloc] initWithInterval:1.0
                                           repeats:YES
                                             async:YES
                                             block:^(YJTimer * _Nonnull timer) {
        NSLog(@"timer running---%@", [NSThread currentThread]);
    }];
    [self.timer start];
}
- (void)dealloc {
    [self.timer cancel];
}
@end

// 打印
09:50:01.948312+0800 Demo[24436:9961956] timer running---<NSThread: 0x6000026a8140>{number = 8, name = (null)}
09:50:02.949507+0800 Demo[24436:9961960] timer running---<NSThread: 0x6000026aed40>{number = 9, name = (null)}
09:50:03.949519+0800 Demo[24436:9961960] timer running---<NSThread: 0x6000026aed40>{number = 9, name = (null)}

3,说明

  • 定时器在运行或取消状态下,调用dispatch_resume函数会crash

  • 它不受runloop的影响,所以比NSTimerCADisplayLink都准时

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。