Jak korzystać z NSTimer?

Odpowiedzi:

616

Po pierwsze chciałbym zwrócić uwagę na dokumentację Cocoa / CF (która zawsze jest świetnym pierwszym punktem kontaktowym). Dokumenty Apple mają sekcję u góry każdego artykułu zatytułowanego „Przewodniki towarzyszące”, w której znajdują się przewodniki dotyczące dokumentowanego tematu (jeśli takie istnieją). Na przykład, z NSTimer, dokumentacja zawiera dwie przewodników towarzyszących:

W tej sytuacji najbardziej przydatne będzie artykuł dotyczący programowania czasowego, podczas gdy tematy wątków są powiązane, ale nie najbardziej bezpośrednio związane z dokumentowaną klasą. Jeśli spojrzysz na artykuł Tematy programowania czasowego, jest on podzielony na dwie części:

  • Timery
  • Korzystanie z timerów

W przypadku artykułów, które przyjmują ten format, często znajduje się przegląd klasy i jej zastosowania, a następnie przykładowy kod, jak go używać, w tym przypadku w sekcji „Korzystanie z timerów”. Istnieją sekcje na temat „Tworzenie i planowanie timera”, „Zatrzymywanie timera” i „Zarządzanie pamięcią”. Z artykułu, tworzenie zaplanowanego, niepowtarzalnego timera można zrobić w następujący sposób:

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

Spowoduje to utworzenie licznika, który jest uruchamiany po 2,0 sekundach i wywołuje targetMethod:się selfz jednym argumentem, który jest wskaźnikiem do NSTimerinstancji.

Jeśli chcesz przyjrzeć się bardziej szczegółowo metodzie, możesz odwołać się do dokumentacji, aby uzyskać więcej informacji, ale kod zawiera również wyjaśnienia.

Jeśli chcesz zatrzymać licznik czasu, który się powtarza (lub zatrzymać niepowtarzalny licznik czasu przed jego uruchomieniem), musisz zachować wskaźnik do utworzonej NSTimerinstancji; często musi to być zmienna instancji, aby można było odwoływać się do niej w innej metodzie. Następnie można nazwać invalidatena NSTimerprzykład:

[myTimer invalidate];
myTimer = nil;

Dobrą praktyką jest także nilwykluczanie zmiennej instancji (na przykład, jeśli twoja metoda unieważniająca licznik czasu jest wywoływana więcej niż jeden raz, a zmienna instancji nie została ustawiona, nila NSTimerinstancja została cofnięta, spowoduje to wyjątek).

Zwróć też uwagę na punkt dotyczący zarządzania pamięcią na dole tego artykułu:

Ponieważ pętla uruchamiania utrzymuje stoper, z punktu widzenia zarządzania pamięcią zazwyczaj nie ma potrzeby utrzymywania odniesienia do timera po jego zaplanowaniu . Ponieważ licznik czasu jest przekazywany jako argument, gdy określasz jego metodę jako selektor, możesz unieważnić powtarzający się licznik czasu, jeśli jest to właściwe w ramach tej metody . Jednak w wielu sytuacjach, chcesz także opcję unieważnienia timera - być może nawet przed jego uruchomieniem. W takim przypadku musisz zachować odniesienie do timera, abyś mógł w razie potrzeby wysłać mu unieważnioną wiadomość. Jeśli utworzysz nieplanowany licznik czasu (patrz „Nieplanowane liczniki czasu”), musisz zachować silne odniesienie do licznika czasu (w środowisku zliczanym referencjami, zachowujesz go), aby nie został zwolniony przed użyciem.

Alex Różański
źródło
Okk, jedno pytanie, co bym wstawił jako mój kod, który byłby wykonywany co dwie sekundy?
lab12
Można by przejść YESdo repeats:kiedy dzwonisz scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:. Jeśli to zrobisz, pamiętaj, aby zachować odwołanie do NSTimerinstancji (jest zwracane przez metodę) i postępuj zgodnie z punktem Zarządzania pamięcią, jak opisano powyżej.
Alex Różański
Nie, gdzie mam umieścić kod, który będzie uruchamiany co 2 sekundy? Na przykład, powiedzmy, że chcę, aby wydawał sygnał dźwiękowy co 2 sekundy. gdzie mam umieścić kod dźwiękowy sygnału dźwiękowego ..?
lab12
W metodzie określonej za pomocą targeti selector. Na przykład, jeśli twoim celem jest, selfa selektorem jest timerMethod:, metoda wywoływana podczas odpalania timera jest timerMethod:zdefiniowana na self. Następnie możesz wstawić dowolny kod do tej metody, a metoda będzie wywoływana przy każdym uruchomieniu timera. Zauważ, że metoda wywoływana podczas uruchamiania timera (którą podajesz jako selector:) może przyjąć tylko jeden argument (który po wywołaniu jest wskaźnikiem do NSTimerinstancji).
Alex Różański
Przepraszamy, oznaczało „zdefiniowano w self
Alex Rozanski,
331

istnieje kilka sposobów korzystania z timera:

1) Zaplanowany timer i użycie selektora

NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 2.0
                      target: self
                      selector:@selector(onTick:)
                      userInfo: nil repeats:NO];
  • jeśli ustawisz powtarzanie na NIE, licznik zaczeka 2 sekundy przed uruchomieniem selektora, a następnie zatrzyma się;
  • w przypadku powtórzenia: TAK, licznik czasu uruchomi się natychmiast i będzie powtarzał wywołanie selektora co 2 sekundy;
  • aby zatrzymać stoper, wywołujesz metodę timera -invalidate: [t unieważnij];

Na marginesie, zamiast używać timera, który się nie powtarza i wywołuje selektor po określonym czasie, możesz użyć prostej instrukcji takiej jak ta:

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

będzie to miało taki sam efekt jak powyższy przykładowy kod; ale jeśli chcesz wywoływać selektor co n-ty raz, używasz timera z powtórzeniami: TAK;

2) samowyzwalacz czasowy

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];
  • spowoduje to utworzenie timera, który uruchomi się w określonym przez Ciebie niestandardowym terminie (w tym przypadku po minucie) i będzie się powtarzał co sekundę

3) nieplanowany zegar i użycie wywołania

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];

a następnie ręcznie uruchamiasz stoper, gdy potrzebujesz:

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



Uwaga: metoda onTick: wygląda następująco:

-(void)onTick:(NSTimer *)timer {
   //do smth
}
Woofy
źródło
Ok, ale widzisz, że chcę obniżyć przezroczystość mojej aplikacji, więc nie wiem, jak to zastosować za pomocą NSTimer
lab12
24
Jezu, ci ludzie dzisiaj ... głosujcie ode mnie, bo nie sprecyzowaliście tego od samego początku i piszmy na próżno!
Woofy,
28
Nie pisałeś na próżno. To jest DOBRA informacja!
willc2
Jak w metodzie 2 „samowyzwalacz czasowy” mogę zatrzymać stoper, kiedy chcę?
Satyam
1
@Satyamsvv, możesz zatrzymać stoper, wywołując, powiedzmy inną metodę: [timer validate]; timer = zero;
Kimpoy
59

Coś takiego:

NSTimer *timer;

    timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
                     target: self
                     selector: @selector(handleTimer:)
                     userInfo: nil
                     repeats: YES];
ennuikiller
źródło
14
Ciągle silny ... 2014 też!
Muruganandham K
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
Dan
źródło
W jakiś sposób tworzy cykl przechowywania, więc MyViewControllernigdy nie zostaje zwolniony.
Darrarski
5
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(timerCalled) userInfo:nil repeats:NO];

-(void)timerCalled
{
     NSLog(@"Timer Called");
     // Your Code
}
Mohit
źródło
gdzie to napisałeś?
Mohit
1
@JigneshB ta odpowiedź dotyczy tylko sposobu używania NSTimer, a nie używania go w tle
Mohit
Napisałem w tle, tj. - (void) applicationDidEnterBackground: (UIApplication *) application {}
Jignesh B
z trybem powtarzanym TAK
Jignesh B
Dokumentacja Apple'a jest bardzo szczegółowa na temat podpisu metody wywołania zwrotnego. Twoje jest złe.
Nikolai Ruhe
2

W odpowiedziach brakuje określonego timera dnia tutaj jest na następną godzinę:

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];

Oczywiście zamień „doRefresh” na pożądaną przez klasę metodę

spróbuj raz utworzyć obiekt kalendarza i uczyń allUnits statycznym dla zwiększenia wydajności.

dodanie komponentu godzinnego działa dobrze, nie ma potrzeby testu północy ( link )

gjpc
źródło