NSTimer

作者: 小五92 | 来源:发表于2018-11-08 16:31 被阅读18次

    NSTimer的属性

    + @property(readonly, getter=isValid) BOOL valid :
    

    返回Boolean 表示当前的timer是否还有效。

    + @property(copy) NSDate *fireDate :
    

    定时器的触发时间。如果定时器已经无效,则返回最后一次的启动时间。也可以通过该属性来改变定时器的触发时间。

    + @property(readonly) NSTimeInterval timeInterval :
    

    返回定时器的时间间隔。如果定时器的repeat为NO,则返回0。

    + @property(readonly, retain) id userInfo :
    

    返回定时器的userInfo 对象,如果定时器已经失效,则无权访问,所以用之前,先通过valid 来检测定时器是否有效。

    NSTimer的方法

    类方法

    1、以scheduled(安排)开头的方法,该类型方法创建的定时器,已经将定时器以默认的运行模式(NSDefaultRunLoopMode)安排到当前到run loop 中。即,表示不需要下面的方法手动将定时器加到run loop中。

    - (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode;
    
    • 当创建后,repeat设为NO,则会在当前的NSDate,延迟interval后,执行一次。但是调用fire方法,则会立马执行。
    • repeat 设为YES,则每间隔interval,执行一次。
    //interval表示时间间隔,
    //repeats 表示是否重复执行,
    //block中是定时器的执行代码。
    + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
    
    //target 表示当前定时器启动时,接受aSelecttor消息的对象,并且定时器会对target 强引用,
    //selector 表示定时器启动时发送给target的消息,
    //userInfo 表示定时器的用户信息,同样会强引用,一般为nil,
    + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
    
    //invocation 是NSInvocation类型,当定时启动时,通过该对象进行消息转发(调用某个对象的消息),同样也是强引用。
    + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
    

    2、需要手动加到runloop中,如果不加,不执行;调用fire方法则会执行一次,无论repeat是否为ture。

    + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
    
    + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
    
    + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
    

    实例方法

    1、创建定时器,需要手动加到run loop中。

    //date:定时器启动的时间。
    - (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
    
    - (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(id)ui repeats:(BOOL)rep;
    

    2、

    • (void)fire:启动定时器的方法。
    • (void)invalidate :销毁定时器。

    总结

    • 有block 的方法都是iOS10++ 才有,并且不存在循环引用的问题(已验证)。
    • 以scheduled 开头的方法,不需要手动将定时器加到runloop中,其他方法都需要。
    • 如果不加到run loop ,无论repeat是否为true ,都不会执行;调用fire ,只会执行一次,无内存泄漏的问题。

    NSTimer的循环引用问题的本质是:

    NSTimer在初始化的时候是放在VC方法中的,而VC的self又是作为NSTimer对象的一个参数存在的,就导致了一个死循环。

    解决问题的本质:打破NSTimer对当前View的持有。

    解决问题的方法:

    1、调用invalidate方法,销毁定时器。切记:不可在VC的delloc方法中调用,循环引用不会走到delloc。
    2、通过代理弱引用:

    • 创建middleWeak类,定义protocol,及弱引用delegate,定义timeAction方法,并在该方法中调用代理的方法;
    • 目标VC中,实现该代理,创建NStimer实例时,将target设为middleWeak,selector为timeAction。
    • 过程:定时器到执行middleWeak中的timeAction,然后回调目标VC的代理方法。

    3、利用NSProxy消息转发

    • 创建middleNSProxy,实现methodSignatureForSelector 和 forwardInvocation,并创建weak修饰的id类型的target
    • 创建middleNSProxy实例,并将proxy的target设为self。
    • 将time的target设为middleNSproxy。
    @interface ceshiProxy : NSProxy
    
    @property (nonatomic, weak) id aTarget;      // 此对象要从外部传过来
    
    @end
    
    //.m
    #import "ceshiProxy.h"
    
    @implementation ceshiProxy
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
        return [self.aTarget methodSignatureForSelector:sel];
    }
    
    - (void)forwardInvocation:(NSInvocation *)invocation {
        [invocation invokeWithTarget:self.aTarget];
    }
    
    @end
    
    //使用
    ceshiProxy *proxy = [ceshiProxy alloc];
    proxy.aTarget = self; 
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:proxy selector:@selector(timerFire) userInfo:nil repeats:YES];
    

    4、利用新的API
    在iOS10以后,提供了三种新的API
    scheduledTimerWithTimeInterval:repeats:block:
    timerWithTimeInterval:repeats:block:
    initWithFireDate:interval:repeats:block:

    解决列表滑动,Timer不执行的问题:

    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
    原因:当滑动ScrollView的时候,NSRunloop的mode并不是NSDefaultRunLoopMode,而是UITrackingRunLoopMode,为此,我们需要设置一个包含既包含NSDefaultRunLoopMode又包含UITrackingRunLoopMode的mode,那就是NSRunLoopCommonModes。

    一位大神总结的Runloop知识点:https://blog.ibireme.com/2015/05/18/runloop/

    相关文章

      网友评论

          本文标题:NSTimer

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