Calcolo del numero di secondi tra due punti nel tempo, in Cocoa, anche quando l'orologio del sistema è cambiato a metà

StackOverflow https://stackoverflow.com/questions/1627764

Domanda

Sto scrivendo un programma per l'utente finale Cocoa OS X (Leopard 10.5+) che utilizza timestamp per calcolare le statistiche per quanto tempo qualcosa viene visualizzato sullo schermo.Il tempo viene calcolato periodicamente mentre il programma viene eseguito utilizzando un NSTimer ripetuto. [NSDate date] viene utilizzato per acquisire timestamp, Inizio E Fine.Calcolare la differenza tra le due date in secondi è banale.

Si verifica un problema se un utente finale o un ntp modifica l'orologio del sistema. [NSDate date] si basa sull'orologio di sistema, quindi se è cambiato, il file Fine la variabile sarà distorta rispetto a Inizio, confondendo notevolmente il calcolo del tempo.La mia domanda:

1.Come posso calcolare con precisione il tempo che intercorre tra? Inizio E Fine, in secondi, anche quando l'orologio di sistema viene modificato a metà?

Penso che ho bisogno di un punto di riferimento immutabile nel tempo in modo da poter calcolare quanti secondi sono trascorsi da allora.Ad esempio, il tempo di attività del sistema.10.6 ha - (NSTimeInterval)systemUptime, parte di NSProcessInfo, che fornisce il tempo di attività del sistema.Tuttavia, questo non funzionerà poiché la mia app deve funzionare nella versione 10.5.

Ho provato a creare un contatore del tempo utilizzando NSImer, ma questo non è accurato.NSImer ha diverse modalità di esecuzione e può eseguirne solo una alla volta.NSTimer (per impostazione predefinita) viene inserito nel file predefinito modalità di esecuzione.Se un utente inizia a manipolare l'interfaccia utente per un tempo sufficientemente lungo, entrerà NSEventTrackingRunLoopMode e salta il predefinito modalità di esecuzione, che può far sì che le attivazioni di NSTimer vengano saltate, rendendolo un modo impreciso di contare i secondi.

Ho anche pensato di creare un thread separato (NSRunLoop) per eseguire un contatore dei secondi NSImer, tenendolo lontano dalle interazioni dell'interfaccia utente.Ma sono molto nuovo nel multi-threading e mi piacerebbe starne lontano, se possibile.Inoltre, non sono sicuro che funzionerebbe correttamente nel caso in cui la CPU venga bloccata da un'altra applicazione (Photoshop che esegue il rendering di un'immagine di grandi dimensioni, ecc...), causando il blocco del mio NSRunLoop per un tempo sufficiente a rovinarne il funzionamento. NSTimer.

Apprezzo qualsiasi aiuto.:)

È stato utile?

Soluzione 3

Ho trovato un modo per farlo utilizzando la funzione UpTime () C, fornita in <CoreServices/CoreServices.h>. Ciò restituisce Absolute Time (specifico per CPU), che può essere facilmente convertito in Duration Time (millisecondi o nanosecondi). Dettagli qui: http://www.meandmark.com/timingpart1.html (guarda sotto la parte 3 per UpTime)

Non sono riuscito a far funzionare correttamente mach_absolute_time(), probabilmente a causa della mia mancanza di conoscenza al riguardo e della mancanza di documentazione sul web in merito. Sembra afferrare contemporaneamente UpTime(), ma convertendolo in un doppio mi ha lasciato sbalordito.

[[NSApp currentEvent] timestamp] ha funzionato, ma solo se l'applicazione stava ricevendo NSEvents. Se l'applicazione passasse in primo piano, non riceverebbe eventi e <=> continuerebbe semplicemente a restituire ripetutamente lo stesso vecchio timestamp in un metodo di attivazione NSTimer, fino a quando l'utente finale non decidesse di interagire nuovamente con l'app.

Grazie per tutto il tuo aiuto Marc e Mike! Entrambi mi avete sicuramente inviato nella giusta direzione che porta alla risposta. :)

Altri suggerimenti

A seconda di cosa guida questo codice, hai 2 possibilità:

  • Per una precisione assoluta, utilizzare mach_absolute_time() . Darà l'intervallo di tempo esattamente tra i punti in cui hai chiamato la funzione.
  • Ma in un'app GUI, questo è spesso in realtà indesiderabile. Invece, vuoi la differenza di tempo tra gli eventi che hanno iniziato e terminato la tua durata. In tal caso, confronta [[NSApp currentEvent] timestamp]

Ok, quindi è un azzardo, ma potresti provare a implementare qualcosa di simile NSSystemClockDidChangeNotification disponibile in Snow Leopard.

Quindi abbi pazienza con me, perché questa è un'idea strana ed è decisamente non deterministica.Ma cosa succederebbe se avessi un thread di watchdog che corre per tutta la durata del tuo programma?Questo thread, ogni n secondi, leggerebbe l'ora del sistema e la memorizzerebbe.Per amor di discussione, facciamo solo 5 secondi.Quindi ogni 5 secondi confronta la lettura precedente con l'ora corrente del sistema.Se c'è una differenza "abbastanza grande" ("abbastanza grande" dovrebbe essere sicuramente maggiore di 5, ma non troppo maggiore, per tenere conto del non determinismo della pianificazione del processo e della priorità dei thread), pubblicare una notifica che indica che si è verificato un cambiamento temporale significativo.Dovresti giocare con il fuzzing del valore che costituisce "abbastanza grande" (o abbastanza piccolo, se l'orologio è stato reimpostato su un orario precedente) per le tue esigenze di precisione.

So che è un po' complicato, ma escludendo qualsiasi altra soluzione, cosa ne pensi?Potrebbe questo, o qualcosa del genere, risolvere il tuo problema?

Modificare

Ok, quindi hai modificato la tua domanda originale per dire che preferiresti non utilizzare un thread di watchdog perché sei nuovo al multithreading.Capisco la paura di fare qualcosa di un po' più avanzato di quanto ti senti a tuo agio, ma questa potrebbe rivelarsi l'unica soluzione.In tal caso, potresti avere un po’ di lettura da fare.=)

E sì, lo so che qualcosa come Photoshop che blocca il processore è un problema.Un'altra soluzione (ancora più complicata) sarebbe quella di avere un cane da guardia invece di farlo filo, hanno un watchdog separato processi che ha la massima priorità, quindi è un po' più immune al pegging del processore.Ma ancora una volta, la situazione sta diventando davvero complicata.

Modifica finale

Lascerò tutte le mie altre idee sopra per completezza, ma sembra che anche l'utilizzo del tempo di attività del sistema sarà un modo valido per affrontare questo problema.Da [[NSProcessInfo processInfo] systemUptime] funziona solo con la versione 10.6+, puoi semplicemente chiamare mach_absolute_time().Per accedere a quella funzione, basta #include <mach/mach_time.h>.Dovrebbe essere lo stesso valore restituito da NSProcessInfo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top