J'essaie d'utiliser NSTIMER pour créer une minuterie de style d'arrêt qui incrémente toutes les 0,1 secondes, mais il semble courir trop vite parfois.
C'est comme ça que je l'ai fait: P> et ensuite: p> -(void)updateTimeLabel
{
maxTime=maxTime+0.1;
timerLabel.text =[NSString stringWithFormat:@"%.1f Seconds",maxTime];
}
4 Réponses :
Selon le NSTIMER < / code>
documentation, il n'est pas censé être précis. P>
En raison des différentes sources d'entrée, une boucle d'exécution typique gère, la résolution effective de l'intervalle de temps pour une minuterie est limitée à l'ordre de 50-100 millisecondes. Si le temps de cuisson d'une minuterie se produit pendant une longue callait ou si la boucle d'exécution est en mode qui ne surveille pas la minuterie, la minuterie ne tire pas avant la prochaine fois que la boucle d'exécution vérifie la minuterie. Par conséquent, le temps réel à laquelle les tirs de la minuterie peuvent potentiellement être une période de temps significative après le temps de tir prévu. P> blockQuote>
Vous voudrez peut-être utiliser le
DISPATTATCH_ADER CODE>
fonction de GCD, qui est suggéré par le Documentation officielle Pour ce but exact (Création d'une minuterie). P>Si vous souhaitez effectuer un bloc une fois après un intervalle de temps spécifié, vous pouvez utiliser le
Dispatch_After code> ou
DisPatch_After_f Code> Fonction. P> blockQuote>
Au fait, je suis d'accord avec la réponse de Caleb. Vous allez probablement résoudre vos problèmes si vous n'accumulez pas d'erreur comme votre faire en ce moment. Si vous stockez la date de début et recalculez l'heure à chaque itération à l'aide du
-imeInterveinceinceinceince: code>, vous allez retrouver avec une mise à jour de l'interface utilisateur précise, quelle que soit la précision de la minuterie. P>
Gabriele - Je n'ai pas besoin de "temps-car depuis", car je dois afficher le temps continuellement à l'utilisateur dans une étiquette ...
Vous en avez besoin pour ne pas accumuler d'erreur. Si vous recompagnez le temps de chaque "itération", l'erreur n'est pas accumulée. Sinon, vous résumez chaque imprécision de Nstimer code>, finissant par une mesure très imprécise.
Nstimer n'est pas "imprécis" si vous utilisez une répétition.
@Hotlicks: Avez-vous une référence pour cette réclamation? Pourquoi l'option de répétition pourrait-elle nier l'accumulation d'erreur?
NSTIMER n'est pas garanti d'être précis, bien que dans la pratique, c'est généralement (si vous ne faites rien d'autre sur votre fil principal ...). Cependant, il est parfaitement raisonnable de mettre à jour un affichage ... n'utilisez tout simplement pas le rappel pour calculer votre minuterie. Enregistrez l'heure actuelle lorsque vous démarrez votre minuterie et obtenez la différence entre maintenant et lorsque vous avez commencé chaque fois que la minuterie incendie. Ensuite, ce n'est pas vraiment important à quel point NSTimer est précisément la cuisson, cela n'a aucune incidence sur combien de fois une seconde que votre écran affichage à l'écran. P>
maxTime = [[NSDate date] timeIntervalSince:startDate];
Mais je suppose que le déclencheur NSTIMER est la chose qui se passe toutes les 0,1 secondes, et c'est augmenter Maxtime - Maxtime suit la minuterie et ne le définit pas
Je comprends, mais la minuterie peut être éteinte de 50 ms d'une manière ou d'une autre. Ce n'est pas une forte erreur pour beaucoup de choses, mais cela va ajouter rapidement. Personne ne se soucie de la mise à jour de l'affichage exactement i> tous les 0,1 seconde, mais lorsque vous faire i> se déplacer pour mettre à jour l'affichage, il devrait être aussi précis que possible. Vous y réalisez cela en calculant en fonction de la différence entre l'heure actuelle et une heure de départ fixe, évitez donc l'accumulation d'erreur.
Voici une classe que vous pouvez utiliser pour faire ce que vous voulez:
@interface StopWatch() @property ( nonatomic, strong ) NSTimer * displayTimer ; @property ( nonatomic ) CFAbsoluteTime startTime ; @end @implementation StopWatch -(void)dealloc { [ self.displayTimer invalidate ] ; } -(void)startTimer { self.startTime = CFAbsoluteTimeGetCurrent() ; self.displayTimer = [ NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector( timerFired: ) userInfo:nil repeats:YES ] ; } -(void)stopTimer { [ self.displayTimer invalidate ] ; self.displayTimer = nil ; CFAbsoluteTime elapsedTime = CFAbsoluteTimeGetCurrent() - self.startTime ; [ self updateDisplay:elapsedTime ] ; } -(void)timerFired:(NSTimer*)timer { CFAbsoluteTime elapsedTime = CFAbsoluteTimeGetCurrent() - self.startTime ; [ self updateDisplay:elapsedTime ] ; } -(void)updateDisplay:(CFAbsoluteTime)elapsedTime { // update your label here } @end
Salut Niels ... semble fonctionner, mais l'étiquette affiche une très grande valeur, même si j'ai utilisé la valeur relative pour le temps écoulé ... Toute idée pourquoi?
+1 Mais quelques réflexions: 1. Vous ne pouvez pas invalider code> la minuterie dans
dealloc code>, car la forte référence de la minuterie empêchera
dealloc code> de toujours être appelé. Vous avez un cycle de référence fort ici. Vous devriez probablement créer la minuterie dans
ViewWillappear code> et
Invalidate code> dans
ViewWilldisappear code>. 2. Je pourrais également suggérer un
cadisplaylink code> plutôt qu'un
NSTIMER code>, mais l'idée est la même.
En d'autres termes (comme j'ai compris) ... Je n'ai pas vraiment besoin de passer «écailles» à la méthode d'affichage, car il est viré avec précision maintenant? Mon UpdateTimeMabel initial fonctionnerait toujours depuis que la mise à jour est bien en utilisant la différence absolue dans TimerFired.
@ user2308343 Je pense que le message à emporter de la réponse de Nielsbot est que vous ne devriez pas maintenir votre propre comptoir. Vous ne devriez pas compter sur la précision des minuteries. Vous devez appeler votre minuterie avec une fréquence suffisamment élevée pour obtenir l'UX souhaité, mais calculer le temps écoulé en utilisant des différences entre le démarrage cfabsoluTimegetCurrent () code> et la valeur actuelle.
ce que @rob a dit. Aussi @rob: J'ai écrit un programme simple pour tester cela et -Dealloc code> est appelé. Je pense qu'utiliser
cadisplaylink () code> pourrait être une surenvéralisation de la CPU, mais vous obtiendrez un affichage à l'écran plus précis, non?
@ user2308343 Vérifiez votre code d'affichage pour les bugs. Comment formatez-vous la valeur de l'heure à l'affichage? J'ai testé ce code et cela donne le résultat correct pour moi.
@nielsbot sur le cadisplayLink code>, deux observations: 1. Si vous utilisez
nsrunloopcommonModes code> Vous pouvez continuer à fonctionner pendant que d'autres choses se passent. Par exemple, si vous faites défiler une tableView en même temps, la minuterie standard s'arrête jusqu'à ce que le défilement soit effectué. 2. Si vous êtes inquiet pour la charge de calcul, vous pouvez définir le cadre code> (code> code> sur une valeur plus élevée. Mais cela n'est pas appelé avant que ce soit prêt à redessiner l'écran, donc je ne serais donc pas inquiet pour les frais généraux de calcul. Il ne devrait éventuellement pas mettre à jour son étiquette si la chaîne de temps a changé, de toute façon (même si vous utilisez la minuterie).
@nielsbot sur le Invalidate code> Dans
DealLoc code>, ce modèle d'essayer d'invalider une minuterie répétée dans
DealLoc code> est un cycle de conservation commun. (La méthode
DEALLOC CODE> est appelée lorsqu'il n'y a pas de références plus fortes, essayez donc de résoudre la référence code> de la minuterie code> dans une méthode
dealloc code> T appelé jusqu'à ce qu'il n'y ait plus de références plus fortes ne fonctionnera pas.) Dans mon test,
DealLoc code> n'est pas appelé. (Ne me trompe pas. Sinon j'aime bien votre réponse.)
Dans quelle méthode crée-tu la minuterie?
Si vous êtes des événements de synchronisation, vous devez appeler
cfabsoluTimeGetCurrent () code> lorsque vous démarrez votre minuterie, appelez-la à nouveau à la fin de l'intervalle. Soustrayez les 2 valeurs et vous obtiendrez le temps écoulé en quelques secondes.
C'est-à-dire que le temps réel écoulé est indépendant du temps que vous affichez. Vous pouvez donc mettre à jour votre écran environ tous les 0,1, mais cela n'a pas vraiment d'importance si vos mises à jour sont désactivées un peu. Ensuite, lorsque votre minuterie est en pause, mettez à nouveau la mise à jour de l'écran à l'aide de la méthode dans mon dernier commentaire.
De plus, si vous souhaitez que votre affichage met à jour au moins tous les 0,1, je voudrais mettre ma minuterie pour mettre à jour chaque 0,05.
Rob - la minuterie est créée dans le fichier * .h et initialisée avec ViewDidLoadLoad
Niels - Non, comme vous le voyez, je présente le temps en direct dans une étiquette
J'ai posté le code qui illustre ce que vous voulez.
Les répétitions doivent être assez précises si vous tenez la bouche droite.
"Le problème est qu'il fonctionne très précisément." -- C'est un problème?
@ user2308343 Vous dites que votre minuterie est initialisée dans
ViewDidLoad code>. C'est OK. Mon point principal est que vous ne voulez pas essayer de
invalider code> la minuterie dans
dealloc code>. Vous faites généralement cela dans
ViewWilldisappear code>. Et pour des raisons de symétrie, vous voudrez donc planifier la minuterie dans
ViewDidAppear code> plutôt que
ViewDidLoad code>. (Si vous, par exemple, appuyez sur une autre scène, vous obtiendrez
Viewwilldisappear code>, mais lorsque vous revenez,
ViewDidLoad code> pas i> est appelé Encore une fois, d'où le modèle commun pour le faire dans
ViewWillappear code>.)