Adakah cara mudah untuk mendapatkan waktu dengan sangat tepat?
Saya perlu menghitung beberapa penundaan antara panggilan metode. Lebih khusus lagi, saya ingin menghitung kecepatan scrolling di UIScrollView.
Adakah cara mudah untuk mendapatkan waktu dengan sangat tepat?
Saya perlu menghitung beberapa penundaan antara panggilan metode. Lebih khusus lagi, saya ingin menghitung kecepatan scrolling di UIScrollView.
Jawaban:
NSDate
dan timeIntervalSince*
metode akan mengembalikan NSTimeInterval
nilai ganda dengan akurasi sub-milidetik. NSTimeInterval
dalam hitungan detik, tapi menggunakan ganda untuk memberi Anda presisi yang lebih baik.
Untuk menghitung akurasi waktu milidetik, Anda dapat melakukan:
// Get a current time for where you want to start measuring from
NSDate *date = [NSDate date];
// do work...
// Find elapsed time and convert to milliseconds
// Use (-) modifier to conversion since receiver is earlier than now
double timePassed_ms = [date timeIntervalSinceNow] * -1000.0;
Dokumentasi tentang timeIntervalSinceNow .
Ada banyak cara lain untuk menghitung interval ini menggunakan NSDate
, dan saya akan merekomendasikan melihat dokumentasi kelas NSDate
yang ditemukan di Referensi Kelas NSDate .
NSDate
dan mach_absolute_time()
pada level sekitar 30ms. 27 vs. 29, 36 vs. 39, 43 vs. 45. NSDate
lebih mudah digunakan bagi saya dan hasilnya cukup mirip untuk tidak peduli.
mach_absolute_time()
dapat digunakan untuk mendapatkan pengukuran yang tepat.
Lihat http://developer.apple.com/qa/qa2004/qa1398.html
Juga tersedia CACurrentMediaTime()
, yang pada dasarnya sama tetapi dengan antarmuka yang lebih mudah digunakan.
(Catatan: Jawaban ini ditulis pada tahun 2009. Lihat jawaban Pavel Alexeev untuk clock_gettime()
antarmuka POSIX yang lebih sederhana yang tersedia di versi macOS dan iOS yang lebih baru.)
CACurrentMediaTime()
, yang mach_absolute_time()
langsung diubah menjadi file double
.
elapsedNano
adalah tipe Nanoseconds
, yang bukan tipe integer biasa. Ini adalah alias dari UnsignedWide
, yang merupakan struct dengan dua bidang integer 32-bit. Anda dapat menggunakan UnsignedWideToUInt64()
sebagai pengganti cast jika mau.
Jangan gunakan NSDate
, CFAbsoluteTimeGetCurrent
atau gettimeofday
untuk mengukur waktu berlalu. Ini semua bergantung pada jam sistem, yang dapat berubah kapan saja karena berbagai alasan, seperti sinkronisasi waktu jaringan (NTP) memperbarui jam (sering terjadi untuk menyesuaikan penyimpangan), penyesuaian DST, detik kabisat, dan sebagainya.
Ini berarti bahwa jika Anda mengukur kecepatan unduh atau unggah, Anda akan mendapatkan lonjakan atau penurunan tiba-tiba pada angka Anda yang tidak berkorelasi dengan apa yang sebenarnya terjadi; tes kinerja Anda akan memiliki pencilan aneh yang salah; dan timer manual Anda akan terpicu setelah durasi yang salah. Waktu bahkan mungkin mundur, dan Anda berakhir dengan delta negatif, dan Anda bisa berakhir dengan rekursi tak terbatas atau kode mati (ya, saya telah melakukan keduanya).
Gunakan mach_absolute_time
. Ini mengukur detik nyata sejak kernel di-boot. Ini meningkat secara monoton (tidak akan pernah mundur), dan tidak terpengaruh oleh pengaturan tanggal dan waktu. Karena merepotkan untuk dikerjakan, berikut bungkus sederhana yang memberi Anda NSTimeInterval
:
// LBClock.h
@interface LBClock : NSObject
+ (instancetype)sharedClock;
// since device boot or something. Monotonically increasing, unaffected by date and time settings
- (NSTimeInterval)absoluteTime;
- (NSTimeInterval)machAbsoluteToTimeInterval:(uint64_t)machAbsolute;
@end
// LBClock.m
#include <mach/mach.h>
#include <mach/mach_time.h>
@implementation LBClock
{
mach_timebase_info_data_t _clock_timebase;
}
+ (instancetype)sharedClock
{
static LBClock *g;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
g = [LBClock new];
});
return g;
}
- (id)init
{
if(!(self = [super init]))
return nil;
mach_timebase_info(&_clock_timebase);
return self;
}
- (NSTimeInterval)machAbsoluteToTimeInterval:(uint64_t)machAbsolute
{
uint64_t nanos = (machAbsolute * _clock_timebase.numer) / _clock_timebase.denom;
return nanos/1.0e9;
}
- (NSTimeInterval)absoluteTime
{
uint64_t machtime = mach_absolute_time();
return [self machAbsoluteToTimeInterval:machtime];
}
@end
CACurrentMediaTime()
QuartzCore?
@import Darwin;
bukannya#include <mach/mach_time.h>
CFAbsoluteTimeGetCurrent()
mengembalikan waktu absolut sebagai sebuah double
nilai, tetapi saya tidak tahu apa ketepatannya - mungkin hanya memperbarui setiap lusin milidetik, atau mungkin memperbarui setiap mikrodetik, saya tidak tahu.
72.89674947369
detik akan sangat tidak umum, mengingat semua nilai lain yang mungkin terjadi. ;)
Saya TIDAK akan menggunakan mach_absolute_time()
karena menanyakan kombinasi kernel dan prosesor untuk waktu absolut menggunakan kutu (mungkin uptime).
Apa yang akan saya gunakan:
CFAbsoluteTimeGetCurrent();
Fungsi ini dioptimalkan untuk memperbaiki perbedaan pada perangkat lunak dan perangkat keras iOS dan OSX.
Sesuatu yang lebih Geekier
Hasil bagi dari selisih mach_absolute_time()
dan AFAbsoluteTimeGetCurrent()
selalu sekitar 24000011.154871
Ini adalah log aplikasi saya:
Harap dicatat bahwa waktu hasil akhir perbedaan di CFAbsoluteTimeGetCurrent()
's
2012-03-19 21:46:35.609 Rest Counter[3776:707] First Time: 353900795.609040
2012-03-19 21:46:36.360 Rest Counter[3776:707] Second Time: 353900796.360177
2012-03-19 21:46:36.361 Rest Counter[3776:707] Final Result Time (difference): 0.751137
2012-03-19 21:46:36.363 Rest Counter[3776:707] Mach absolute time: 18027372
2012-03-19 21:46:36.365 Rest Counter[3776:707] Mach absolute time/final time: 24000113.153295
2012-03-19 21:46:36.367 Rest Counter[3776:707] -----------------------------------------------------
2012-03-19 21:46:43.074 Rest Counter[3776:707] First Time: 353900803.074637
2012-03-19 21:46:43.170 Rest Counter[3776:707] Second Time: 353900803.170256
2012-03-19 21:46:43.172 Rest Counter[3776:707] Final Result Time (difference): 0.095619
2012-03-19 21:46:43.173 Rest Counter[3776:707] Mach absolute time: 2294833
2012-03-19 21:46:43.175 Rest Counter[3776:707] Mach absolute time/final time: 23999753.727777
2012-03-19 21:46:43.177 Rest Counter[3776:707] -----------------------------------------------------
2012-03-19 21:46:46.499 Rest Counter[3776:707] First Time: 353900806.499199
2012-03-19 21:46:55.017 Rest Counter[3776:707] Second Time: 353900815.016985
2012-03-19 21:46:55.018 Rest Counter[3776:707] Final Result Time (difference): 8.517786
2012-03-19 21:46:55.020 Rest Counter[3776:707] Mach absolute time: 204426836
2012-03-19 21:46:55.022 Rest Counter[3776:707] Mach absolute time/final time: 23999996.639500
2012-03-19 21:46:55.024 Rest Counter[3776:707] -----------------------------------------------------
mach_absolute_time()
dengan mach_timebase_info_data
dan kemudian melakukannya (long double)((mach_absolute_time()*time_base.numer)/((1000*1000)*time_base.denom));
. Untuk mendapatkan basis waktu mach, Anda dapat melakukannya(void)mach_timebase_info(&your_timebase);
#define CTTimeStart() NSDate * __date = [NSDate date]
#define CTTimeEnd(MSG) NSLog(MSG " %g",[__date timeIntervalSinceNow]*-1)
Pemakaian:
CTTimeStart();
...
CTTimeEnd(@"that was a long time:");
Keluaran:
2013-08-23 15:34:39.558 App-Dev[21229:907] that was a long time: .0023
NSDate
bergantung pada jam sistem yang dapat diubah kapan saja, berpotensi menghasilkan interval waktu yang salah atau bahkan negatif. Gunakan mach_absolute_time
sebagai gantinya untuk mendapatkan waktu berlalu yang benar.
mach_absolute_time
dapat terpengaruh oleh reboot perangkat. Gunakan waktu server sebagai gantinya.
Selain itu, berikut adalah cara menghitung 64-bit yang NSNumber
diinisialisasi dengan epoch Unix dalam milidetik, jika Anda ingin menyimpannya di CoreData. Saya membutuhkan ini untuk aplikasi saya yang berinteraksi dengan sistem yang menyimpan tanggal dengan cara ini.
+ (NSNumber*) longUnixEpoch {
return [NSNumber numberWithLongLong:[[NSDate date] timeIntervalSince1970] * 1000];
}
Fungsi berdasarkan mach_absolute_time
bagus untuk pengukuran singkat.
Namun untuk pengukuran yang lama, peringatan penting adalah bahwa mereka berhenti berdetak saat perangkat sedang tidur.
Ada fungsi untuk mendapatkan waktu sejak boot. Itu tidak berhenti saat tidur. Juga, gettimeofday
ini tidak monotonik, tetapi dalam percobaan saya, saya selalu melihat bahwa waktu boot berubah ketika waktu sistem berubah, jadi saya pikir itu akan bekerja dengan baik.
func timeSinceBoot() -> TimeInterval
{
var bootTime = timeval()
var currentTime = timeval()
var timeZone = timezone()
let mib = UnsafeMutablePointer<Int32>.allocate(capacity: 2)
mib[0] = CTL_KERN
mib[1] = KERN_BOOTTIME
var size = MemoryLayout.size(ofValue: bootTime)
var timeSinceBoot = 0.0
gettimeofday(¤tTime, &timeZone)
if sysctl(mib, 2, &bootTime, &size, nil, 0) != -1 && bootTime.tv_sec != 0 {
timeSinceBoot = Double(currentTime.tv_sec - bootTime.tv_sec)
timeSinceBoot += Double(currentTime.tv_usec - bootTime.tv_usec) / 1000000.0
}
return timeSinceBoot
}
Dan karena iOS 10 dan macOS 10.12 kita dapat menggunakan CLOCK_MONOTONIC:
if #available(OSX 10.12, *) {
var uptime = timespec()
if clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) == 0 {
return Double(uptime.tv_sec) + Double(uptime.tv_nsec) / 1000000000.0
}
}
Singkatnya:
Date.timeIntervalSinceReferenceDate
- berubah ketika waktu sistem berubah, tidak monotonikCFAbsoluteTimeGetCurrent()
- tidak monotonik, mungkin mundurCACurrentMediaTime()
- berhenti berdetak saat perangkat tidurtimeSinceBoot()
- tidak tidur, tapi mungkin tidak monotonikCLOCK_MONOTONIC
- tidak tidur, monoton, didukung sejak iOS 10Saya tahu ini adalah yang lama tetapi bahkan saya mendapati diri saya melewatinya lagi, jadi saya pikir saya akan mengirimkan pilihan saya sendiri di sini.
Taruhan terbaik adalah memeriksa posting blog saya tentang ini : Pengaturan waktu di Objective-C: Sebuah stopwatch
Pada dasarnya, saya menulis kelas yang berhenti menonton dengan cara yang sangat dasar tetapi dikemas sehingga Anda hanya perlu melakukan hal berikut:
[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];
Dan Anda berakhir dengan:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
di log ...
Sekali lagi, periksa posting saya untuk lebih banyak atau unduh di sini: MMStopwatch.zip
NSDate
bergantung pada jam sistem yang dapat diubah kapan saja, berpotensi menghasilkan interval waktu yang salah atau bahkan negatif. Gunakan mach_absolute_time
sebagai gantinya untuk mendapatkan waktu berlalu yang benar.
Anda bisa mendapatkan waktu saat ini dalam milidetik sejak 1 Januari 1970 menggunakan NSDate:
- (double)currentTimeInMilliseconds {
NSDate *date = [NSDate date];
return [date timeIntervalSince1970]*1000;
}
Bagi mereka, kami membutuhkan versi Swift dari jawaban @Jeff Thompson:
// Get a current time for where you want to start measuring from
var date = NSDate()
// do work...
// Find elapsed time and convert to milliseconds
// Use (-) modifier to conversion since receiver is earlier than now
var timePassed_ms: Double = date.timeIntervalSinceNow * -1000.0
Saya harap ini membantu Anda.
NSDate
tergantung pada jam sistem yang dapat diubah kapan saja, berpotensi menghasilkan interval waktu yang salah atau bahkan negatif. Menggunakanmach_absolute_time
sebagai gantinya untuk mendapatkan waktu berlalu yang benar.