NSTimer的使用

什么timer,timer如何使用

timer即为计时器,在给定的时间间隔后调用给定的方法。
有以下三种创建方式:

  • 第一种方式:先创建一个timer,然后自己使用runloop注册
+ (NSTimer *)timerWithTimeInterval:(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 *)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));
  • 第二种方式:在当前的runloop中调度timer
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
  • 第三种方式:使用给定的fireData初始化一个timer
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(nullable id)ui repeats:(BOOL)rep NS_DESIGNATED_INITIALIZER;

如何配置参数

在所有的创建timer方法中,你可以告诉timer当它在执行(fires)的时候要对哪个Object发送哪个message,也可以提供user info字典,可以在字典中放入timer在fire时调用的method所需要的有用的信息。
有两种方式告诉timer这些配置信息:

  • target-selector,要符合- (void)targetMethod:(NSTimer*)theTimer这种签名(方法名可以随意)。
  • NSInvocation实例。

timer引用对象的生命周期

因为是runloop操纵引用着timer,从对象生命周期的角度来看,通常没有必要在scheduled后保存timer的引用。因为timer会当成参数传入到你指定的selector中,你可以在selector中使timer无效(invalidate)。然而在很多情况下,你想在timer start之前就想让它无效(invalidate),在这种情况下,你需要保存timer的引用,从而达到可以在任意的合适的时候停止timer。当你创建了一个没有被scheduled的timer时,你需要对timer做一个强引用,以至于在使用timer之前它没有被释放掉。

timer保存着target的强引用,这就意味着只要timer依然有效,target就不会被销毁,因此在targt中的dealloc中尝试让timer无效是没有任何意义的,因为dealloc在timer有效时永远不会被调用。

示例

代码:

@interface TimerExmaple()
/**特点:
1、重复计时的timer
2、创建时就被当前runloop调度
保存弱引用,以便在适合的时机使它无效
 **/
@property (weak) NSTimer *repeatTimer;
/**
 1、unregistered的timer
 没有runloop引用它,所以保持强引用,以保证在timer开始之前它是没有被销毁的
 **/
@property (strong) NSTimer *unregisteredTimer;

//用来计数使用fireData创建的timer的调用次数
@property NSUInteger timerCount;
@end

@implementation TimerExmaple

/**
 特点:
 1、timer创建后就被当前runloop调度
 2、timer只会执行一次method
 **/
- (IBAction)startOneOffTimer:(id)sender{
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(targetMethod:) userInfo:[self userInfo] repeats:NO];
}

/**
 重复使用同一个计时器
 在重新开启一个计数器之前要保证上一个计时器无效
 只能使用invalidate使计时器无效,移出runloop
 **/
- (IBAction)startRepeatingTimer{
    //使之前的timer无效
    [self.repeatTimer invalidate];
    
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(targetMethod:) userInfo:[self userInfo] repeats:YES];
    
    //保存引用
    self.repeatTimer = timer;
}

/**
 创建timer,但没有调度
 使用NSInvocation可以调用非- (void)targetMethod:(NSTimer*)theTimer的方法签名
 **/
- (IBAction)createUnregisterdTimer {
    NSMethodSignature *methodSignature = [self methodSignatureForSelector:@selector(invocationMethod:)];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
    [invocation setTarget:self];
    [invocation setSelector:@selector(invocationMethod:)];
    NSDate *startDate = [NSDate date];
    [invocation setArgument:&startDate atIndex:2];
    
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 invocation:invocation repeats:YES];
    self.unregisteredTimer = timer;
}

//调度上面创建的timer
- (IBAction)startUnregisteredTimer{
    if (self.unregisteredTimer != nil) {
        NSRunLoop *runloop = [NSRunLoop currentRunLoop];
        [runloop addTimer:self.unregisteredTimer forMode:NSDefaultRunLoopMode];
    }
}

/**
 创建fireDate的runloop
 在fireDate时立即执行timer
 调用fire可以立即执行timer,在repeat为YES的情况下,不会妨碍计时器后期的执行计划
 在repeat为NO的情况下timer会被视为执行完毕,然后销毁
 **/
- (IBAction)startFireDateTimer {
    NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:0.0];
    NSTimer *timer = [[NSTimer alloc] initWithFireDate:fireDate interval:2.0 target:self selector:@selector(countedTimerFireMethod:) userInfo:[self userInfo] repeats:YES];
    self.timerCount = 1;
    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    [runloop addTimer:timer forMode:NSDefaultRunLoopMode];
}

- (IBAction)stopReapeatingTimer {
    [self.repeatTimer invalidate];
    self.repeatTimer = nil;
}

- (IBAction)stopUnregisteredTimer {
    [self.unregisteredTimer invalidate];
    self.unregisteredTimer = nil;
}

- (NSDictionary *)userInfo {
    return @{@"StartDate": [NSDate date]};
}

- (void)invocationMethod:(NSDate *)date {
    NSLog(@"invocation method start on %@", date);
}

- (void)countedTimerFireMethod:(NSTimer *)timer {
    NSDate *startDate = [[timer userInfo] objectForKey:@"StartDate"];
    NSLog(@"Timer started on %@; current %@; fire count %lu", startDate, [NSDate date], (unsigned long)self.timerCount);
    
    self.timerCount++;
    if (self.timerCount > 3) {
        [timer invalidate];
    }
}

- (void)targetMethod:(NSTimer *)timer {
    NSDate *startDate = [[timer userInfo] objectForKey:@"StartDate"];
    NSLog(@"Timer started on %@; current %@", startDate, [NSDate date]);
}

注意点

1、将计时器注册到runloop后并不会立马执行,要经过给定的时间间隔后才执行

2017-02-06 11:30:38.901 IOSAPILearn[6339:371431] Timer started on 2017-02-06 03:30:36 +0000; current 2017-02-06 03:30:38 +0000

2、如果想立马执行计时器,可以使用fire方法,或者使用initWithFireDate来指定立即执行时间。
3、只有invalid可以使计时器无效,将计时器移除runloop。

你可能感兴趣的:(NSTimer的使用)