美文网首页iOS UI开发
iOS实战-更精准的定时器

iOS实战-更精准的定时器

作者: GemShi | 来源:发表于2019-02-08 17:02 被阅读9次

iOS中,常用的定时器有三种:NSTimer,CADisplayLink,GCD。在一定基础之上,做进一步探究。

NSTimer,CADisplayLink

在使用scheduleTimerWithTimeInterval:target:selector:userInfo:repeats:方式创建的定时器会以默认方式添加到当前线程runloop中,无需手动添加。
如果有需求:点击屏幕触发定时器,不需要时点击返回

@interface TestViewController ()

@property(nonatomic,strong)NSTimer *timer;

@end

@implementation TestViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
}

-(void)run
{
    NSLog(@"%s",__func__);
}

-(void)dealloc
{
    [self.timer invalidate];
    self.timer = nil;
    
    NSLog(@"%s",__func__);
}

@end

经测试,这段代码dealloc方法不会执行,存在循环引用。因为NSTimer内部有强指针target,所以不管外部传入的是weak还是strong,都会将传入的内存地址赋值给形参target,所以不管target存储的是weak的地址还是strong的地址,NSTimer对target都是强引用,所以timer和self产生了循环引用。

通过GNUStep中源码可以看出,传入的object被retain一次,被timer强持有。

NSTimer schedule:target: init

代码所产生的循环引用

timer-self循环引用

要想解决这个循环引用,使其中一个强引用变成弱引用。


打破循环引用
  • 方法一:使用block创建方式
    block存储在timer中,timer对block强引用,block对self是弱引用,定时器对self是弱引用。
__weak typeof(self)wself = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
  [wself run];
}];
  • 方法二
    如果使用scheduleTimerWithTimeInterval:target:方法,就要引入中间变量,将中间对象的指针置为弱指针。
@interface SSProxy : NSObject
//弱指针,打破循环引用
@property(nonatomic, weak)id target;

+(instancetype)proxyWithTarget:(id)target;

@end

@implementation SSProxy

+(instancetype)proxyWithTarget:(id)target
{
    SSProxy *proxy = [[SSProxy alloc] init];
    proxy.target = target;
    return proxy;
}
//消息转发阶段,返回值不为空,直接给某个对象发送消息
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    return self.target;
}

@end
@interface TestViewController ()

@property(nonatomic,strong)NSTimer *timer;

@end

@implementation TestViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[SSProxy proxyWithTarget:self] selector:@selector(run) userInfo:nil repeats:YES];
}

-(void)run
{
    NSLog(@"%s",__func__);
}

-(void)dealloc
{
    [self.timer invalidate];
    self.timer = nil;
    
    NSLog(@"%s",__func__);
}

@end
  • 方法三:使用CADisplayLink
@interface TestViewController ()

@property(nonatomic,strong)CADisplayLink *timer;

@end

@implementation TestViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.timer = [CADisplayLink displayLinkWithTarget:[SSProxy proxyWithTarget:self] selector:@selector(run)];
    [self.timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}

-(void)run
{
    NSLog(@"%s",__func__);
}

-(void)dealloc
{
    [self.timer invalidate];
    self.timer = nil;
    
    NSLog(@"%s",__func__);
}

@end

存在的弊端:
1.NSTimer和CADisplayLink底层是runloop实现的,所以有可能并不准时,如果runloop任务过于繁重,每一圈的处理就会耗时,就导致不准时。
2.对于CADisplayLink,当CPU忙于其他计算,就无法保证每秒60次的频率执行屏幕绘制。

GCD定时器

GCD定时器直接和系统内核挂钩,不依赖于runloop,相对精准。

@interface TestViewController ()

@property(nonatomic,strong)dispatch_source_t timer;

@end

@implementation TestViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  dispatch_queue_t queue = 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_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    
  dispatch_source_set_event_handler(timer, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
    
  dispatch_resume(timer);
    
  self.timer = timer;
}

-(void)run
{
    NSLog(@"%s",__func__);
}

-(void)dealloc
{
    NSLog(@"%s",__func__);
}

@end

相关文章

  • iOS实战-更精准的定时器

    iOS中,常用的定时器有三种:NSTimer,CADisplayLink,GCD。在一定基础之上,做进一步探究。 ...

  • 无标题文章

    iOS NSTimer使用详解-开启、关闭、移除 定时器定时器详解ios定时器关闭定时器NSTimer 1、要使用...

  • iOS进阶-谈谈定时器

    目录 iOS提供定时器API 定时器开发中的坑 一、 iOS提供定时器API 二、定时器开发中的坑 2.1、必须办...

  • 定时器

    定时器 兼容ios

  • iOS中的定时器

    点击这里>> cocoaChina: iOS中的定时器 iOS中定时器有三种,分别是NSTimer、CADispl...

  • tableView或者scrollView滑动时 定时器 不响应

    先写解决办法 1. 2. 使用GCD创建定时器。GCD创建定时器不收Runloop的影响,并且GCD的定时器更精准...

  • GCD定时器使用

    iOS中的常用定时器分为这几类: NSTimer CADisplayLink GCD定时器 选择GCD定时器原因:...

  • iOS:NSTimer的几种创建方式

    在iOS开发中,经常会用到定时器,iOS中常用的定时器有三种:NSTimer,GCD,CADisplayLink。...

  • 每日笔记

    1、通过safari打开网页 2、iOS的几种定时器及区别 iOS的几种定时器及区别 3、long long类型 ...

  • iOS Timer

    iOS开发中定时器经常会用到,iOS中常用的定时器有三种,分别是NSTime,CADisplayLink和GCD。...

网友评论

    本文标题:iOS实战-更精准的定时器

    本文链接:https://www.haomeiwen.com/subject/cpxosqtx.html