如何使用NSTimer?


Answers:


616

首先,我想引起您对Cocoa / CF文档的关注(这总是一个很好的第一口)。Apple文档在每篇参考文章的顶部都有一个名为“伴侣指南”的部分,其中列出了所记录主题的指南(如果有的话)。例如,使用NSTimer文档列出了两个配套指南:

对于您的情况,“计时器编程主题”文章可能是最有用的,而线程主题是相关的,但与所记录的类没有最直接的关系。如果您查看“计时器编程主题”文章,它分为两个部分:

  • 计时器
  • 使用计时器

对于采用这种格式的文章,通常会对该类及其用途进行概述,然后在“使用计时器”部分中提供一些有关如何使用它的示例代码。有关于“创建和计划计时器”,“停止计时器”和“内存管理”的部分。从本文开始,可以创建一个计划的,非重复的计时器,如下所示:

[NSTimer scheduledTimerWithTimeInterval:2.0
    target:self
    selector:@selector(targetMethod:)
    userInfo:nil
    repeats:NO];

这将创建为2.0秒后发射并调用计时器targetMethod:self有一个说法,这是一个指向NSTimer实例。

然后,如果您想更详细地了解该方法,可以参考文档以获取更多信息,但是代码周围也有解释。

如果要停止一个重复的计时器(或在触发前停止一个非重复计时器),则需要保留一个指向NSTimer所创建实例的指针;通常,这将需要是一个实例变量,以便您可以在另一种方法中引用它。然后,您可以调用invalidateNSTimer实例:

[myTimer invalidate];
myTimer = nil;

nil淘汰实例变量也是一种好习惯(例如,如果使计时器无效的方法被多次调用且实例变量尚未设置为nilNSTimer实例已被释放,则它将引发异常)。

还请注意文章底部的“内存管理”要点:

因为运行循环会维护计时器,所以从内存管理的角度来看,通常无需在计划好计时器后保留对计时器的引用。由于在将计时器的方法指定为选择器时将计时器作为参数传递,因此可以在该方法中的适当时使重复计时器无效。但是,在许多情况下,您还可能希望使计时器无效(甚至可能在计时器启动之前)。在这种情况下,您确实需要保留对计时器的引用,以便可以在适当的时候向其发送无效消息。。如果创建了计划外的计时器(请参阅“计划外的计时器”),则必须维护对计时器的强引用(在引用计数的环境中,请保留该计时器),以便在使用前不释放该计时器。


好的,一个问题,我每两秒钟执行一次的代码是什么?
09年

你会传递YESrepeats:,当你调用scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:。如果这样做,请确保保留对该NSTimer实例的引用(该实例由方法返回),并按照上面我详细介绍的“内存管理”中的说明进行操作。
Alex Rozanski

不,我将每2秒启动一次的代码放在哪里?例如,假设我希望它每2秒发出一次哔声。我会在哪里放置蜂鸣声代码..?
2009年

在您使用target和指定的方法中selector。例如,如果目标是self和选择是timerMethod:,当计时器触发被调用的方法timerMethod:所定义的self。然后,您可以在该方法中放入所需的任何代码,并在计时器触发时调用该方法。请注意,在计时器启动时调用的方法(您以形式传入selector:)只能采用一个参数(调用时是指向NSTimer实例的指针)。
Alex Rozanski

抱歉,意思是“定义于self
Alex Rozanski

331

有几种使用计时器的方法:

1)定时计时器和使用选择器

NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 2.0
                      target: self
                      selector:@selector(onTick:)
                      userInfo: nil repeats:NO];
  • 如果您将重复次数设置为“否”,则计时器将在运行选择器之前等待2秒钟,之后它将停止;
  • 如果重复:是,计时器将立即启动,并每2秒重复调用一次选择器;
  • 要停止计时器,您可以调用计时器的-invalidate方法:[t invalidate];

附带说明一下,您可以使用一个简单的语句,而不是使用不重复并在指定间隔后调用选择器的计时器:

[self performSelector:@selector(onTick:) withObject:nil afterDelay:2.0];

这将与上面的示例代码具有相同的效果;但是如果您想每隔n次调用选择器,则可以使用带有重复的计时器:是;

2)自定定时器

NSDate *d = [NSDate dateWithTimeIntervalSinceNow: 60.0];
NSTimer *t = [[NSTimer alloc] initWithFireDate: d
                              interval: 1
                              target: self
                              selector:@selector(onTick:)
                              userInfo:nil repeats:YES];

NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer:t forMode: NSDefaultRunLoopMode];
[t release];
  • 这将创建一个计时器,该计时器将在您指定的自定义日期(在这种情况下为一分钟)启动,并每秒钟重复一次

3)计划外计时器和使用调用

NSMethodSignature *sgn = [self methodSignatureForSelector:@selector(onTick:)];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature: sgn];
[inv setTarget: self];
[inv setSelector:@selector(onTick:)];

NSTimer *t = [NSTimer timerWithTimeInterval: 1.0
                      invocation:inv 
                      repeats:YES];

然后,您可以在需要时手动启动计时器,如下所示:

NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer: t forMode: NSDefaultRunLoopMode];



并请注意,onTick:方法如下所示:

-(void)onTick:(NSTimer *)timer {
   //do smth
}

好的,但是您看到我想降低应用程序的透明度,所以我不知道如何在NSTimer中应用它
lab12

24
哎呀,今天这些人..投我反对票,因为您从一开始就没有指定,让我们徒劳!
Woofy

28
你没有白费。这是好信息!
willc2

在方法2“自定义计时器”中,如何在需要时停止计时器?
萨蒂扬

1
@Satyamsvv,您可以通过调用停止计时器,比如说另一种方法:[timer invalidate]; 计时器=零;
Kimpoy 2012年

59

像这样:

NSTimer *timer;

    timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
                     target: self
                     selector: @selector(handleTimer:)
                     userInfo: nil
                     repeats: YES];

14
仍然表现强劲……2014年也是如此!
Muruganandham K 2014年

5
#import "MyViewController.h"

@interface MyViewController ()

@property (strong, nonatomic) NSTimer *timer;

@end

@implementation MyViewController

double timerInterval = 1.0f;

- (NSTimer *) timer {
    if (!_timer) {
        _timer = [NSTimer timerWithTimeInterval:timerInterval target:self selector:@selector(onTick:) userInfo:nil repeats:YES];
    }
    return _timer;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}

-(void)onTick:(NSTimer*)timer
{
    NSLog(@"Tick...");
}

@end

它以某种方式创建了保留周期,因此MyViewController永不释放。
Darrarski 2015年

5
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(timerCalled) userInfo:nil repeats:NO];

-(void)timerCalled
{
     NSLog(@"Timer Called");
     // Your Code
}

你在哪里写的?
Mohit

1
@JigneshB这个答案只是关于如何使用NSTimer而不是在后台使用
Mohit14年

我是在后台方法中编写的,即-(void)applicationDidEnterBackground:(UIApplication *)application {}
Jignesh B 2014年

重复模式为YES
Jignesh B 2014年

Apple的文档非常详细地介绍了回调方法的签名。你错了
Nikolai Ruhe 2015年

2

答案缺少一天中的特定时间计时器,这里是下一个小时:

NSCalendarUnit allUnits = NSCalendarUnitYear   | NSCalendarUnitMonth |
                          NSCalendarUnitDay    | NSCalendarUnitHour  |
                          NSCalendarUnitMinute | NSCalendarUnitSecond;

NSCalendar *calendar = [[ NSCalendar alloc]  
                          initWithCalendarIdentifier:NSGregorianCalendar];

NSDateComponents *weekdayComponents = [calendar components: allUnits 
                                                  fromDate: [ NSDate date ] ];

[ weekdayComponents setHour: weekdayComponents.hour + 1 ];
[ weekdayComponents setMinute: 0 ];
[ weekdayComponents setSecond: 0 ];

NSDate *nextTime = [ calendar dateFromComponents: weekdayComponents ];

refreshTimer = [[ NSTimer alloc ] initWithFireDate: nextTime
                                          interval: 0.0
                                            target: self
                                          selector: @selector( doRefresh )
                                          userInfo: nil repeats: NO ];

[[NSRunLoop currentRunLoop] addTimer: refreshTimer forMode: NSDefaultRunLoopMode];

当然,用类的所需方法替换“ doRefresh”

尝试一次创建日历对象,并使allUnits成为静态对象以提高效率。

将一个小时的组件添加一小时就可以了,不需要进行午夜测试(link

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.