Как мне использовать NSTimer?

344

Как я могу использовать NSTimer? Кто-нибудь может дать мне пошаговые инструкции?

lab12
источник
1
я получил параметрическую информацию на сайте pravinmagdum.wordpress.com/2010/12/30/how-to-use-nstimer . Может быть полезно для вас

Ответы:

616

Во-первых, я хотел бы обратить ваше внимание на документацию по Какао / CF (которая всегда является отличным первым портом захода). Документы Apple имеют раздел в верхней части каждой справочной статьи под названием «Сопутствующие руководства», в котором перечислены руководства по задокументированной теме (если таковые имеются). Например, с NSTimer, в документации списки двух гидов - компаньонов:

В вашей ситуации статья «Темы программирования таймера», вероятно, будет самой полезной, хотя темы с потоками связаны, но не имеют непосредственного отношения к документируемому классу. Если вы посмотрите на статью «Темы программирования таймера», она разделена на две части:

  • Таймеры
  • Использование таймеров

Для статей, использующих этот формат, часто есть обзор класса и того, для чего он используется, а затем пример кода, как его использовать, в данном случае в разделе «Использование таймеров». Существуют разделы «Создание и планирование таймера», «Остановка таймера» и «Управление памятью». Из этой статьи создание запланированного неповторяющегося таймера можно сделать примерно так:

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

Это позволит создать таймер , который запускаемый после 2,0 секунд и вызывает targetMethod:на selfс одним аргументом, который является указателем на NSTimerэкземпляр.

Если затем вы хотите более подробно взглянуть на метод, вы можете обратиться к документации для получения дополнительной информации, но есть и объяснения вокруг кода.

Если вы хотите остановить повторяющийся таймер (или остановить неповторяющийся таймер до его запуска), вам необходимо сохранить указатель на NSTimerсозданный экземпляр; часто это должна быть переменная экземпляра, чтобы вы могли ссылаться на нее в другом методе. После этого вы можете позвонить invalidateпо NSTimerпримеру:

[myTimer invalidate];
myTimer = nil;

Также хорошей практикой является nilисключение переменной экземпляра (например, если ваш метод, который делает недействительным таймер, вызывается более одного раза, а переменная экземпляра не была установлена ​​в значение nilи NSTimerэкземпляр был освобожден, он вызовет исключение).

Обратите также внимание на пункт «Управление памятью» в нижней части статьи:

Поскольку цикл выполнения поддерживает таймер, с точки зрения управления памятью обычно нет необходимости сохранять ссылку на таймер после того, как вы запланировали его . Так как таймер передается в качестве аргумента , когда вы укажете свой метод как селектор, . Если вы создаете незапланированный таймер (см. «Незапланированные таймеры»), то вы должны поддерживать надежную ссылку на таймер (в среде с подсчетом ссылок вы сохраняете его), чтобы он не был освобожден перед его использованием. вы можете сделать недействительным повторяющийся таймер, когда это уместно в этом методе . Однако во многих ситуациях вы также хотите отключить таймер - возможно, даже до его запуска. В этом случае вам нужно сохранить ссылку на таймер, чтобы вы могли отправлять ему сообщение о недействительности в случае необходимости.

Алекс Розанский
источник
Хорошо, один вопрос, что бы я вставил в код, который будет выполняться каждые две секунды?
lab12 20.09.09
Вы бы передать YESдля repeats:когда вы звоните scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:. Если вы сделаете это, убедитесь, что сохранили ссылку на NSTimerэкземпляр (он возвращается методом) и следуйте пункту «Управление памятью», как я подробно описал выше.
Алекс Розанский
Нет, куда бы я положил свой код, который будет запускаться каждые 2 секунды? Например, скажем, я хотел, чтобы он издавал звуковой сигнал каждые 2 секунды. где бы я положил код звукового сигнала ..?
lab12 20.09.09
В методе, который вы указываете с помощью targetи selector. Например, если ваша цель selfи селектор timerMethod:, метод, вызываемый при срабатывании таймера, timerMethod:определяется в self. Затем вы можете поместить любой код в этот метод, и метод будет вызываться всякий раз, когда срабатывает таймер. Обратите внимание, что метод, вызываемый при срабатывании таймера (который вы передаете как selector:), может принимать только один аргумент (который при вызове является указателем на NSTimerэкземпляр).
Алекс Розанский
Извините, значит "определено self"
Алекс Розанский,
331

Есть несколько способов использования таймера:

1) запланированный таймер и использование селектора

NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 2.0
                      target: self
                      selector:@selector(onTick:)
                      userInfo: nil repeats:NO];
  • если вы установите для повторов значение NO, таймер будет ждать 2 секунды, прежде чем запустить селектор, и после этого он остановится;
  • если повторить: ДА, таймер запустится немедленно и будет повторять вызов селектора каждые 2 секунды;
  • чтобы остановить таймер, вы вызываете метод -invalidate таймера: [t invalidate];

В качестве примечания, вместо использования таймера, который не повторяется и вызывает селектор после определенного интервала, вы можете использовать простое утверждение, подобное этому:

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

это будет иметь тот же эффект, что и пример кода выше; но если вы хотите вызывать селектор каждый раз, вы используете таймер с повторениями: YES;

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
}
Woofy
источник
Хорошо, но вы видите, я хочу понизить прозрачность своего приложения, поэтому я не знаю, как применить это с NSTimer
lab12
24
Господи, эти люди сегодня ... голосуйте против меня, потому что вы не указали это с самого начала и давайте напрасно напишем!
Woofy
28
Вы не напрасно писали. Это хорошая информация!
willc2
В методе 2 «таймер с автоматическим планированием», как я могу остановить таймер, когда захочу?
Satyam
1
@ Satyamsvv, вы можете остановить таймер, вызвав, скажем, другой метод, имеющий: [timer invalidate]; таймер = ноль;
Кимпой
59

Что-то вроде этого:

NSTimer *timer;

    timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
                     target: self
                     selector: @selector(handleTimer:)
                     userInfo: nil
                     repeats: YES];
ennuikiller
источник
14
Все еще идет сильный ... 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никогда не освобождается.
Даррарски
5
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(timerCalled) userInfo:nil repeats:NO];

-(void)timerCalled
{
     NSLog(@"Timer Called");
     // Your Code
}
Мохит
источник
где ты это написал?
Мохит
1
@JigneshB этот ответ только о том, как использовать NSTimer, а не об использовании его в фоновом режиме
Mohit
я написал в фоновом методе, т.е. - (void) applicationDidEnterBackground: (UIApplication *) application {}
Jignesh B
с повторным режимом ДА
Jignesh B
Документация Apple очень специфична в отношении сигнатуры метода обратного вызова. Твой не прав.
Николай Рухе
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 статическим для эффективности.

добавление одного к часу компонента работает просто отлично, нет необходимости в полуночном тесте ( ссылка )

gjpc
источник