NSTimer

作者: Champion | 来源:发表于2019-03-07 21:54 被阅读16次

    定时器,间隔时间后触发某个事件。
    A timer waits until a certain time interval has elapsed and then fires, sending a specified message to a target object.

    + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
    // Creates a timer and schedules it on the current run loop in the default mode.
    + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
    
    + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
    + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
    // 简单创建一个
    [NSTimer scheduledTimerWithTimeInterval:5.f target:self selector:@selector(run:) userInfo:nil repeats:YES];
    - (void)run:(NSTimer *)timer{
    // do something
    }
    //可以设置任务开始时间
    [_timer setFireDate:[NSDate distantFuture]];  // 未来才会开启,就是关闭嘛
    [_timer setFireDate:[NSDate distantPast]];
    
    注意的小问题
    1、runloop的mode

    用schedule方式启动的timer是add到runloopNSDefaultRunLoopMode中,这就会出现其他mode时timer得不到调度的问题。最常见的问题就是在UITrackingRunLoopMode,即UIScrollView滑动过程中定时器失效。

    解决方式就是把timer add到runloop的NSRunLoopCommonModes。UITrackingRunLoopMode和kCFRunLoopDefaultMode都被标记为了common模式,所以只需要将timer的模式设置为NSRunLoopCommonModes,就可以在默认模式和追踪模式都能够运行。

    2、循环引用

    简单来说就是NSTimer的target被强引用了,而通常target就是所在的控制器,控制器又强引用了timer。

    NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(Timered:) userInfo:nil repeats:YES];
    

    而上面代码,这个timer并没有被self引用,那么为什么self不会被释放呢?因为timer被加到了runloop中,timer又强引用了self,所以timer一直存在的话,self也不会释放。

    敲黑板解决方式

    - (void)viewDidLoad {
        [super viewDidLoad];
        __weak id weakSelf = self;
        NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer *timer) {
            NSLog(@"block %@",weakSelf);
        }];
    }
    
    @implementation NSTimer(BlockTimer)
    + (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats blockTimer:(void (^)(NSTimer *))block{
        NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(timered:) userInfo:[block copy] repeats:repeats];
        return timer;
    }
    
    + (void)timered:(NSTimer*)timer {
        void (^block)(NSTimer *timer)  = timer.userInfo;
        block(timer);
    }
    @end
    

    解释:将强引用的target变成了NSTimer的类对象。类对象本身是单例的,是不会释放的,所以强引用也无所谓。执行的block通过userInfo传递给定时器的响应函数timered:。循环引用被打破的结果是:

    timer的使用者强引用timer。
    timer强引用NSTimer的类对象。
    timer的使用者在block中通过weak的形式使用,因此是被timer弱引用。

    2.timer的invalidate方法的具体作用参考苹果官方,这个方法会停止timer并将其从RunLoop中移除。
    This method is the only way to remove a timer from an [NSRunLoop]object. The NSRunLoop object removes its strong reference to the timer, either just before the [invalidate] method returns or at some later point.

    @interface TimerViewController ()
    @property (nonatomic, strong) NSTimer *timer;
    @end
    
    @implementation TimerViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        __weak typeof(self) weakSelf = self;
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
            [weakSelf timerRun];
        }];
    }
    
    - (void)timerRun {
        NSLog(@"%s", __func__);
    }
    
    - (void)dealloc {
        [self.timer invalidate];
        NSLog(@"%s", __func__);
    }
    

    相关文章

      网友评论

          本文标题:NSTimer

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