美文网首页
定时器nstimer 详解

定时器nstimer 详解

作者: 池鹏程 | 来源:发表于2017-04-21 21:09 被阅读0次

一、主线程开启NSTimer

开启方式:scheduledTimerWithTimeInterval(直接开启)与timerWithTimeInterval(需要加入到RunLoop中进行开启)两种开启方式。
加入RunLoop中的开启方式:
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
二、子线程中开启定时器

timerWithTimeInterval在线程中开启定时器需要在RunLoop中run,在主线中默认开启了。为什么我在退出控制器后,控制器不执行dealloc方法?

- (void)viewWillDisappear:(BOOL)animated{
  [super viewWillDisappear:animated];
  [self.timer invalidate];
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [NSThread detachNewThreadWithBlock:^{
        [self regularTimer1];
    }];
}
- (void)regularTimer1{
  _timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
  [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode]
  [[NSRunLoop currentRunLoop] run];
}

三、定时器循环引用的问题

当scheduledTimerWithTimeInterval使用创建定时器的时候,一定要在viewWillDisappear中将定时器从runloop中移除,否则当退出控制器的时候不能执行dealloc方法。疑点:如果使用timerWithTimeInterval(非block情况下)在runloop中添加定时器,使用invalidate方法,退出控制器后不能执行dealloc方法。

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [self.timer invalidate];
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self regularTimer];
}
- (void)regularTimer{
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}
- (void)timerAction{
    NSLog(@"%s",__func__);
}

如何在使用timerWithTimeInterval(非block情况下)时,防止强引用呢?对NSTimer使用类别,让timerWithTimeInterval不持有self对象。

#import <Foundation/Foundation.h>

@interface NSTimer (MyTimer)
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)timeInterval block:(void(^)())block repeats:(BOOL)repeat;
+ (void)timerAction:(NSTimer *)timer;
@end

#import "NSTimer+MyTimer.h"
@implementation NSTimer (MyTimer)
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)timeInterval block:(void(^)())block repeats:(BOOL)repeat{
    //1、类方法不能调用成员方法
    //2、对self的强引用是类,类不占用内存空间
    return [self timerWithTimeInterval:timeInterval target:self selector:@selector(timerAction:) userInfo:block repeats:repeat];
}
+ (void)timerAction:(NSTimer *)timer{
    NSLog(@"%@",[timer userInfo]);
    void (^block)() = [timer userInfo];
    if (block) {
        block();
    }
}
@end

在控制器中使用NSTimer类别中的+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)timeInterval block:(void(^)())block repeats:(BOOL)repeat;方法创建定时器。

  • (void)dealloc{
    //1、self 的引用计数器为0,自动执行dealloc
    //2、如果不执行invalidate,定时还会在runloop中,NSTimer+MyTimer.h中的timerAction会一直执行
    [_timer invalidate];
    //1、可直接置空,当置空后,timer立即被释放被释放,如果不置空,在等执行dealloc后.
    //2、_timer = nil与self.timer = nil效果一样,具体以后再更新说明。
    //self.timer = nil 的写法
    /*
    - (void)setTimer:(NSTimer *)timer{
    if(_timer != timer){
    [_timer release];
    _timer = [timer retain];
    }
    }
    */
    self.timer = nil;
    NSLog(@"%s",func);
    }
  • (void)viewDidLoad {
    [super viewDidLoad];
    [self regularTimer];
    }
  • (void)regularTimer{
    __weak typeof(self) weakSelf = self;
    _timer = [NSTimer timerWithTimeInterval:1.0f block:^{
    //循环引用 self->timer->block->self
    [weakSelf timerAction];
    } repeats:YES];
    [[NSRunLoop currentRunLoop]addTimer:_timer forMode:NSDefaultRunLoopMode];
    }
  • (void)timerAction{
    NSLog(@"%s",func);
    }

在iOS10中已提供如下API来防止使用timerWithTimeInterVal(非block情况下)对self产生的循环引用。

  • (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));

四、使用GCD来启动定时器

  • (void)timerAction{
    NSLog(@"%s",func);
    }
    /**
    使用多线程创建定时器
    @param timerInterVal 时间间隔
    @param repeat 是否重复
    */
  • (void)gcdTimer:(int)timerInterVal repeats:(BOOL)repeat{
    //自定义一个队列
    dispatch_queue_t queue = dispatch_queue_create("timeQueue", DISPATCH_QUEUE_SERIAL);
    //创建定时器
    dispatch_source_t gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(gcdTimer, DISPATCH_TIME_NOW, timerInterVal * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(gcdTimer, ^{
    NSLog(@"dispatch_source_set_event_handler");
    if (repeat) {
    [self timerAction];
    }else{
    dispatch_source_cancel(gcdTimer);
    //间隔timerInterVal后执行block里面的方法
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timerInterVal * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    NSLog(@"dispatch_after");
    [self timerAction];
    //在这里取消GCD,但是会执行两次(dispatch_after过了timerInterVal秒,定时器的任务还没有取消之前又开始执行了)
    //dispatch_source_cancel(gcdTimer);
    });
    }
    });
    dispatch_resume(gcdTimer);
    }

五、在不同RunLoop模式下创建定时器

使用timerWithTimeInterval方法,需要添加到runloop中才能开启定时器,加入定时器有五种模式:
1.NSDefaultRunLoopMode,默认模式。执行scheduledTimerWithTimeInterval方法,它会自动加入到该模式下。
2.NSConnectionReplyMode,系统内部使用,用户基本不会使用。
3.NSModalPanelRunLoopMode,处理modal panels事件,这是嘛事件?
4.NSEventTrackingRunLoopMode,UIScrollView,UITableView,UICollectionView 滑动是runloop处于此模式下。
5.NSRunLoopCommonModes,普通模式,防止NSEventTrackingRunLoopMode与NSDefaultRunLoopMode之间的切换,可以把定时器加入普通模式下。

相关文章

网友评论

      本文标题:定时器nstimer 详解

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